From 4d1dc726451d28ccbe3b94f7f12b0b74ba7b5660 Mon Sep 17 00:00:00 2001 From: gurke Date: Wed, 28 Aug 2024 10:54:45 +0200 Subject: [PATCH] feat: added support for redis cluster (#1270) refs https://jsw.ibm.com/browse/INSTA-9475 - does not contain cluster support for ioredis! --- .../test-groups/test-ci-autoprofile-task.yaml | 8 +- .../test-groups/test-ci-aws-fargate-task.yaml | 8 +- .../test-groups/test-ci-aws-lambda-task.yaml | 8 +- ...test-ci-azure-container-services-task.yaml | 8 +- .../test-ci-collector-general-task.yaml | 8 +- ...i-collector-tracing-cloud-aws-v2-task.yaml | 8 +- ...i-collector-tracing-cloud-aws-v3-task.yaml | 8 +- ...ci-collector-tracing-cloud-azure-task.yaml | 8 +- ...t-ci-collector-tracing-cloud-gcp-task.yaml | 8 +- ...st-ci-collector-tracing-database-task.yaml | 8 +- ...-ci-collector-tracing-frameworks-task.yaml | 8 +- ...est-ci-collector-tracing-general-task.yaml | 8 +- ...test-ci-collector-tracing-logger-task.yaml | 8 +- ...t-ci-collector-tracing-messaging-task.yaml | 8 +- .../test-ci-collector-tracing-misc-task.yaml | 8 +- ...t-ci-collector-tracing-protocols-task.yaml | 8 +- .../tasks/test-groups/test-ci-core-task.yaml | 8 +- .../test-ci-google-cloud-run-task.yaml | 8 +- .../test-ci-metrics-util-task.yaml | 8 +- .../test-ci-opentelemetry-exporter-task.yaml | 8 +- .../test-ci-opentelemetry-sampler-task.yaml | 8 +- .../test-ci-serverless-collector-task.yaml | 8 +- .../test-groups/test-ci-serverless-task.yaml | 8 +- .../test-ci-shared-metrics-task.yaml | 8 +- .tekton/templates/test-task.yaml.template | 8 +- bin/start-test-containers.js | 3 +- docker-compose-base.yaml | 46 +- .../test/tracing/database/redis/app.js | 268 ++- .../database/redis/connect-via/cluster.js | 74 + .../database/redis/connect-via/default.js | 27 + .../database/redis/connect-via/index.js | 13 + .../test/tracing/database/redis/latestApp.js | 260 --- .../test/tracing/database/redis/legacyApp.js | 2 + .../test/tracing/database/redis/test.js | 1481 +++++++++-------- .../tracing/instrumentation/database/redis.js | 121 +- .../src/util/initializedTooLateHeuristic.js | 1 + .../util/initializedTooLateHeuristic_test.js | 2 +- 37 files changed, 1439 insertions(+), 1059 deletions(-) create mode 100644 packages/collector/test/tracing/database/redis/connect-via/cluster.js create mode 100644 packages/collector/test/tracing/database/redis/connect-via/default.js create mode 100644 packages/collector/test/tracing/database/redis/connect-via/index.js delete mode 100644 packages/collector/test/tracing/database/redis/latestApp.js diff --git a/.tekton/tasks/test-groups/test-ci-autoprofile-task.yaml b/.tekton/tasks/test-groups/test-ci-autoprofile-task.yaml index f5edb08acf..1f9fce6bcd 100644 --- a/.tekton/tasks/test-groups/test-ci-autoprofile-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-autoprofile-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-aws-fargate-task.yaml b/.tekton/tasks/test-groups/test-ci-aws-fargate-task.yaml index 862aa2d3c3..5e207761bd 100644 --- a/.tekton/tasks/test-groups/test-ci-aws-fargate-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-aws-fargate-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-aws-lambda-task.yaml b/.tekton/tasks/test-groups/test-ci-aws-lambda-task.yaml index 25eabdda9f..4372fab2da 100644 --- a/.tekton/tasks/test-groups/test-ci-aws-lambda-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-aws-lambda-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-azure-container-services-task.yaml b/.tekton/tasks/test-groups/test-ci-azure-container-services-task.yaml index 064d993377..5fed6a3a7b 100644 --- a/.tekton/tasks/test-groups/test-ci-azure-container-services-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-azure-container-services-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-general-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-general-task.yaml index 33f394bdf4..d256a4fac4 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-general-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-general-task.yaml @@ -59,7 +59,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -110,6 +115,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v2-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v2-task.yaml index 83299d99a3..059d6aeb49 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v2-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v2-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v3-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v3-task.yaml index b0ed9ec6ca..fbaad85771 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v3-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-aws-v3-task.yaml @@ -39,7 +39,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -90,6 +95,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-azure-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-azure-task.yaml index 2fc74ec35d..de274fa6c3 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-azure-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-azure-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-gcp-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-gcp-task.yaml index d4388235b6..c3436d6c43 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-gcp-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-cloud-gcp-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-database-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-database-task.yaml index 825b886ea7..637efc76ab 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-database-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-database-task.yaml @@ -110,7 +110,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -161,6 +166,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-frameworks-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-frameworks-task.yaml index 36a0d7540f..7bc893a823 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-frameworks-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-frameworks-task.yaml @@ -55,7 +55,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -106,6 +111,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-general-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-general-task.yaml index 8f85cffee6..942d83d80c 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-general-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-general-task.yaml @@ -55,7 +55,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -106,6 +111,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-logger-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-logger-task.yaml index 01bb3e45ea..a78ff39314 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-logger-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-logger-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-messaging-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-messaging-task.yaml index 641b4e2bc5..3b37fced7f 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-messaging-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-messaging-task.yaml @@ -124,7 +124,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -175,6 +180,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-misc-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-misc-task.yaml index 0639a96668..f7a987fd12 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-misc-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-misc-task.yaml @@ -39,7 +39,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -90,6 +95,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-collector-tracing-protocols-task.yaml b/.tekton/tasks/test-groups/test-ci-collector-tracing-protocols-task.yaml index 27ac070884..82e638f830 100644 --- a/.tekton/tasks/test-groups/test-ci-collector-tracing-protocols-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-collector-tracing-protocols-task.yaml @@ -45,7 +45,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -96,6 +101,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-core-task.yaml b/.tekton/tasks/test-groups/test-ci-core-task.yaml index 36d4fddd98..9cd0241b9c 100644 --- a/.tekton/tasks/test-groups/test-ci-core-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-core-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-google-cloud-run-task.yaml b/.tekton/tasks/test-groups/test-ci-google-cloud-run-task.yaml index b6e2347988..14b4c9fcdf 100644 --- a/.tekton/tasks/test-groups/test-ci-google-cloud-run-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-google-cloud-run-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-metrics-util-task.yaml b/.tekton/tasks/test-groups/test-ci-metrics-util-task.yaml index 0aea6af467..1b524ddb80 100644 --- a/.tekton/tasks/test-groups/test-ci-metrics-util-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-metrics-util-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-opentelemetry-exporter-task.yaml b/.tekton/tasks/test-groups/test-ci-opentelemetry-exporter-task.yaml index 21bd71db17..9d332b1dc7 100644 --- a/.tekton/tasks/test-groups/test-ci-opentelemetry-exporter-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-opentelemetry-exporter-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-opentelemetry-sampler-task.yaml b/.tekton/tasks/test-groups/test-ci-opentelemetry-sampler-task.yaml index 96daa88f3f..e80c5116d2 100644 --- a/.tekton/tasks/test-groups/test-ci-opentelemetry-sampler-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-opentelemetry-sampler-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-serverless-collector-task.yaml b/.tekton/tasks/test-groups/test-ci-serverless-collector-task.yaml index 3fdfb63a8f..ac9ff1f055 100644 --- a/.tekton/tasks/test-groups/test-ci-serverless-collector-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-serverless-collector-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-serverless-task.yaml b/.tekton/tasks/test-groups/test-ci-serverless-task.yaml index 27f9d1e26c..6ba9d76669 100644 --- a/.tekton/tasks/test-groups/test-ci-serverless-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-serverless-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/tasks/test-groups/test-ci-shared-metrics-task.yaml b/.tekton/tasks/test-groups/test-ci-shared-metrics-task.yaml index c954db97f0..2348704eec 100644 --- a/.tekton/tasks/test-groups/test-ci-shared-metrics-task.yaml +++ b/.tekton/tasks/test-groups/test-ci-shared-metrics-task.yaml @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -88,6 +93,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" if [ "$(params.esm)" == "true" ]; then diff --git a/.tekton/templates/test-task.yaml.template b/.tekton/templates/test-task.yaml.template index ff73d0169e..980fc7047b 100644 --- a/.tekton/templates/test-task.yaml.template +++ b/.tekton/templates/test-task.yaml.template @@ -37,7 +37,12 @@ spec: valueFrom: secretKeyRef: name: $(params.continuous-delivery-context-secret) - key: "AWS_SECRET_ACCESS_KEY" + key: "AWS_SECRET_ACCESS_KEY" + - name: AZURE_REDIS_CLUSTER_PWD + valueFrom: + secretKeyRef: + name: $(params.continuous-delivery-context-secret) + key: "AZURE_REDIS_CLUSTER_PWD" - name: DB2_CONNECTION_STR valueFrom: secretKeyRef: @@ -94,6 +99,7 @@ spec: export GCP_PROJECT="k8s-brewery" export AZURE_SQL_USERNAME="admin@instana@nodejs-team-db-server" export AZURE_SQL_SERVER="nodejs-team-db-server.database.windows.net" + export AZURE_REDIS_CLUSTER="team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380" export AZURE_SQL_DATABASE="azure-nodejs-test" export AZURE_STORAGE_ACCOUNT_NAME="nodejstracerteam" diff --git a/bin/start-test-containers.js b/bin/start-test-containers.js index cd4141134e..b20be31237 100755 --- a/bin/start-test-containers.js +++ b/bin/start-test-containers.js @@ -30,7 +30,8 @@ if (args.length > 0) { filteredServices = baseConfig.services; } -const filteredConfig = { version: baseConfig.version, services: filteredServices }; +const networks = baseConfig.networks; +const filteredConfig = { version: baseConfig.version, services: filteredServices, networks }; const filteredYaml = yaml.dump(filteredConfig); fs.writeFileSync('./docker-compose.yaml', filteredYaml); diff --git a/docker-compose-base.yaml b/docker-compose-base.yaml index 4a7064721d..47a1830673 100644 --- a/docker-compose-base.yaml +++ b/docker-compose-base.yaml @@ -5,6 +5,43 @@ services: ports: - 6379:6379 + redis-node-0: + image: docker.io/bitnami/redis-cluster:7.2 + environment: + - 'ALLOW_EMPTY_PASSWORD=yes' + - 'REDIS_NODES=172.30.0.2:6379 172.30.0.3:6379 172.30.0.4:6379' + - 'REDIS_CLUSTER_CREATOR=yes' + - 'REDIS_CLUSTER_REPLICAS=0' + - 'BITNAMI_DEBUG=1' + - 'REDIS_CLUSTER_SLEEP_BEFORE_DNS_LOOKUP=10' + ports: + - 6380:6379 + networks: + redis-cluster-network: + ipv4_address: 172.30.0.2 + + redis-node-1: + image: docker.io/bitnami/redis-cluster:7.2 + environment: + - 'ALLOW_EMPTY_PASSWORD=yes' + - 'REDIS_NODES=172.30.0.2:6379 172.30.0.3:6379 172.30.0.4:6379' + ports: + - 6381:6379 + networks: + redis-cluster-network: + ipv4_address: 172.30.0.3 + + redis-node-2: + image: docker.io/bitnami/redis-cluster:7.2 + environment: + - 'ALLOW_EMPTY_PASSWORD=yes' + - 'REDIS_NODES=172.30.0.2:6379 172.30.0.3:6379 172.30.0.4:6379' + ports: + - 6382:6379 + networks: + redis-cluster-network: + ipv4_address: 172.30.0.4 + mongo: image: mongo:4.1.13 ports: @@ -176,4 +213,11 @@ services: - DEBUG=${DEBUG-} - DOCKER_HOST=unix:///var/run/docker.sock volumes: - - "/var/run/docker.sock:/var/run/docker.sock" \ No newline at end of file + - "/var/run/docker.sock:/var/run/docker.sock" + +networks: + redis-cluster-network: + driver: bridge + ipam: + config: + - subnet: 172.30.0.0/16 \ No newline at end of file diff --git a/packages/collector/test/tracing/database/redis/app.js b/packages/collector/test/tracing/database/redis/app.js index 8c5043199e..1da94a24ee 100644 --- a/packages/collector/test/tracing/database/redis/app.js +++ b/packages/collector/test/tracing/database/redis/app.js @@ -1,6 +1,7 @@ /* * (c) Copyright IBM Corp. 2022 */ +/* eslint-disable no-console */ 'use strict'; @@ -11,11 +12,266 @@ process.on('SIGTERM', () => { }); require('./mockVersion'); -const redisLatest = process.env.REDIS_VERSION === 'latest'; +require('../../../..')(); -if (redisLatest) { - require('./latestApp'); -} else { - // v3 - require('./legacyApp'); +const redis = require(process.env.REDIS_PKG); +const bodyParser = require('body-parser'); +const express = require('express'); +const morgan = require('morgan'); +const fetch = require('node-fetch-v2'); +const port = require('../../../test_util/app-port')(); + +const cls = require('../../../../../core/src/tracing/cls'); +const app = express(); +const logPrefix = + `Redis App (version: ${process.env.REDIS_VERSION}, require: ${process.env.REDIS_PKG}, ` + + `cluster: ${process.env.REDIS_CLUSTER}, pid: ${process.pid}):\t`; +const agentPort = process.env.INSTANA_AGENT_PORT; + +let connectedToRedis = false; +let connection; +let connection2; +const connect = require('./connect-via'); + +function log() { + const args = Array.prototype.slice.call(arguments); + args[0] = logPrefix + args[0]; + console.log.apply(console, args); +} + +(async () => { + const { connection1, connection2: _connection2 } = await connect(redis, log); + + connection = connection1; + connection2 = _connection2; + connectedToRedis = true; +})(); + +if (process.env.WITH_STDOUT) { + app.use(morgan(`${logPrefix}:method :url :status`)); } + +app.use(bodyParser.json()); + +app.get('/', (req, res) => { + if (!connectedToRedis) { + res.sendStatus(500); + } else { + res.sendStatus(200); + } +}); + +app.post('/clearkeys', async (req, res) => { + cls.isTracing() && cls.setTracingLevel('0'); + + if (process.env.REDIS_CLUSTER === 'true') { + try { + await Promise.all( + connection.masters.map(async master => { + const client = await connection.nodeClient(master); + await client.flushAll(); + }) + ); + } catch (err) { + log('Failed to flush cluster', err); + res.sendStatus(500); + } + } else { + await connection.flushAll(); + } + + cls.isTracing() && cls.setTracingLevel('1'); + res.sendStatus(200); +}); + +app.post('/values', async (req, res) => { + const key = req.query.key; + const value = req.query.value; + + try { + await connection.set(key, value); + log('Set key successfully.'); + } catch (e) { + log('Set with key %s, value %s failed', key, value, e); + res.sendStatus(500); + } + await fetch(`http://127.0.0.1:${agentPort}`); + log('Sent agent fetch successfully.'); + res.sendStatus(200); +}); + +app.get('/values', async (req, res) => { + const key = req.query.key; + + try { + const redisRes = await connection.get(key); + log('Got redis key successfully.'); + await fetch(`http://127.0.0.1:${agentPort}`); + log('Sent agent request successfully.'); + res.send(redisRes); + } catch (err) { + log('Get with key %s failed', key, err); + res.sendStatus(500); + } +}); + +app.get('/hvals', async (req, res) => { + await connection.hVals('key1'); + await fetch(`http://127.0.0.1:${agentPort}`); + log('Sent agent request successfully.'); + res.sendStatus(200); +}); + +app.get('/blocking', async (req, res) => { + const blPopPromise = connection.blPop(redis.commandOptions({ isolated: true }), 'mykey', 0); + + try { + await connection.lPush('mykey', ['1', '2']); + await blPopPromise; // '2' + + await fetch(`http://127.0.0.1:${agentPort}`); + log('Sent agent request successfully.'); + + res.sendStatus(200); + } catch (err) { + log('Unexpected err', err); + res.sendStatus(500); + } +}); + +app.get('/scan-iterator', async (req, res) => { + // eslint-disable-next-line no-restricted-syntax + for await (const key of connection.scanIterator()) { + try { + await connection.get(key); + } catch (getErr) { + // ignore for now + } + } + + await fetch(`http://127.0.0.1:${agentPort}`); + log('Sent agent request successfully.'); + + res.sendStatus(200); +}); + +app.get('/hset-hget', async (req, res) => { + await connection.hSet('someCollection1', 'key1', 'value1'); + + // HGETALL = hGetAll internally, no need to add test coverage for both + const result = await connection.hGetAll('someCollection1'); + await fetch(`http://127.0.0.1:${agentPort}`); + res.status(200).send(result.key1); +}); + +app.get('/get-without-waiting', (req, res) => { + const key = req.query.key; + connection.get(key); + fetch(`http://127.0.0.1:${agentPort}`).then(() => { + res.sendStatus(200); + }); +}); + +app.get('/set-without-waiting', (req, res) => { + const key = req.query.key; + const value = req.query.value; + + connection.set(key, value); + + fetch(`http://127.0.0.1:${agentPort}`).then(() => { + res.sendStatus(200); + }); +}); + +app.get('/failure', async (req, res) => { + try { + // simulating wrong get usage + const redisRes = await connection.get(null); + res.send(redisRes); + } catch (err) { + await fetch(`http://127.0.0.1:${agentPort}`); + res.sendStatus(500); + } +}); + +app.get('/multi', async (req, res) => { + try { + await connection.multi().set('key', 'value').get('key').exec(); + await fetch(`http://127.0.0.1:${agentPort}`); + res.sendStatus(200); + } catch (err) { + log('Multi failed', err); + res.sendStatus(500); + } +}); + +app.get('/multi-no-waiting', async (req, res) => { + try { + connection.multi().set('key', 'value').get('key').exec(); + await fetch(`http://127.0.0.1:${agentPort}`); + res.sendStatus(200); + } catch (err) { + log('Multi failed', err); + res.sendStatus(500); + } +}); + +app.get('/multiFailure', async (req, res) => { + // simulating wrong get usage + try { + await connection.multi().set('key', 'value').get(null).exec(); + res.sendStatus(500); + } catch (err) { + log('Multi expected to fail', err); + await fetch(`http://127.0.0.1:${agentPort}`); + res.sendStatus(200); + } +}); + +app.get('/batchSuccess', async (req, res) => { + await connection.multi().get('1').set('2', '2').execAsPipeline(); + await fetch(`http://127.0.0.1:${agentPort}`); + res.sendStatus(200); +}); + +app.get('/batchFailure', async (req, res) => { + try { + await connection.multi().get(null).set('2', '2').execAsPipeline(); + res.sendStatus(500); + } catch (err) { + log('Batch expected to fail', err); + await fetch(`http://127.0.0.1:${agentPort}`); + res.sendStatus(200); + } +}); + +app.get('/callSequence', async (req, res) => { + const key = 'foo'; + const value = 'bar'; + + try { + await connection.set(key, value); + const result = await connection.get(key); + await fetch(`http://127.0.0.1:${agentPort}`); + res.send(result); + } catch (err) { + log('Set/Get with key %s, value %s failed', key, value, err); + res.sendStatus(500); + } +}); + +app.post('/two-different-target-hosts', async (req, res) => { + try { + const response = {}; + response.response1 = await connection.set(req.query.key, req.query.value1); + response.response2 = await connection2.set(req.query.key, req.query.value2); + res.json(response); + } catch (e) { + log('Redis set operation failed.', e); + res.sendStatus(500); + } +}); + +app.listen(port, () => { + log(`Listening on port: ${port}`); +}); diff --git a/packages/collector/test/tracing/database/redis/connect-via/cluster.js b/packages/collector/test/tracing/database/redis/connect-via/cluster.js new file mode 100644 index 0000000000..5ea75c2d10 --- /dev/null +++ b/packages/collector/test/tracing/database/redis/connect-via/cluster.js @@ -0,0 +1,74 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +const MAX_TRIES = 50; +const { delay } = require('../../../../../../core/test/test_util'); + +// NOTE: We run the tests locally and on CI against azure redis cluster. +// NOTE: We cannot run redis cluster on Tekton https://github.com/bitnami/charts/issues/28894 +// NOTE: We cannot use a docker based redis cluster at the moment! +// See https://github.com/redis/node-redis/issues/2815. +// These commands are useful as soon as we switch to a docker based cluster. +// node bin/start-test-containers.js --redis-node-0 --redis-node-1 --redis-node-2 +// docker exec -it 2aaaac7b9112 redis-cli -p 6379 cluster info +module.exports = async function connect(redis, log, tries = 0) { + if (!process.env.AZURE_REDIS_CLUSTER || !process.env.AZURE_REDIS_CLUSTER_PWD) { + log( + 'Please set the environment variables AZURE_REDIS_CLUSTER and AZURE_REDIS_CLUSTER_PWD ' + + 'to connect to the cloud redis cluster.' + ); + + process.exit(1); + } + + const nodes = [ + { + url: `redis://${process.env.AZURE_REDIS_CLUSTER}` + } + ]; + + const defaults = { + socket: { + tls: true + }, + password: process.env.AZURE_REDIS_CLUSTER_PWD + }; + + // node bin/start-test-containers.js --redis-node-0 --redis-node-1 --redis-node-2 + // docker exec -it 2aaaac7b9112 redis-cli -p 6379 cluster info + const cluster = redis.createCluster({ + rootNodes: nodes, + useReplicas: false, + defaults + // https://github.com/redis/node-redis/issues/2022 + // maxCommandRedirections: 100 + }); + + cluster.on('error', err => log('Redis Cluster Error', err)); + + log(`Connecting to cluster. (${nodes.map(node => node.url).join(', ')})`); + + try { + await cluster.connect(); + log('Connected to cluster'); + return { connection1: cluster }; + } catch (err) { + log('Failed to connect to cluster', err); + + if (tries >= MAX_TRIES) { + log('Max tries reached, exiting.'); + process.exit(1); + } + + log('Retrying...'); + log('Waiting...'); + await delay(5000); + log('Waited...'); + + tries += 1; + return module.exports.connect(redis, log, tries); + } +}; diff --git a/packages/collector/test/tracing/database/redis/connect-via/default.js b/packages/collector/test/tracing/database/redis/connect-via/default.js new file mode 100644 index 0000000000..3d258c2b44 --- /dev/null +++ b/packages/collector/test/tracing/database/redis/connect-via/default.js @@ -0,0 +1,27 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +module.exports = async function (redis, log) { + const client = redis.createClient({ url: `redis://${process.env.REDIS}` }); + const client2 = redis.createClient({ url: `redis://${process.env.REDIS_ALTERNATIVE}` }); + + [client, client2].forEach(c => { + c.on('error', err => log('Redis Client Error', err)); + }); + + log('Connecting to client 1'); + await client.connect(); + log(`Connected to client 1 (${process.env.REDIS}).`); + + log('Connecting to client 2'); + await client2.connect(); + log(`Connected to client 2 (${process.env.REDIS_ALTERNATIVE}).`); + + return { + connection1: client, + connection2: client2 + }; +}; diff --git a/packages/collector/test/tracing/database/redis/connect-via/index.js b/packages/collector/test/tracing/database/redis/connect-via/index.js new file mode 100644 index 0000000000..1d92750523 --- /dev/null +++ b/packages/collector/test/tracing/database/redis/connect-via/index.js @@ -0,0 +1,13 @@ +/* + * (c) Copyright IBM Corp. 2024 + */ + +'use strict'; + +module.exports = function (redis, log) { + if (process.env.REDIS_CLUSTER === 'true') { + return require('./cluster')(redis, log); + } + + return require('./default')(redis, log); +}; diff --git a/packages/collector/test/tracing/database/redis/latestApp.js b/packages/collector/test/tracing/database/redis/latestApp.js deleted file mode 100644 index df5fefb5f7..0000000000 --- a/packages/collector/test/tracing/database/redis/latestApp.js +++ /dev/null @@ -1,260 +0,0 @@ -/* - * (c) Copyright IBM Corp. 2022 - */ -/* eslint-disable no-console */ - -'use strict'; - -// NOTE: c8 bug https://github.com/bcoe/c8/issues/166 -process.on('SIGTERM', () => { - process.disconnect(); - process.exit(0); -}); - -require('../../../..')(); - -const bodyParser = require('body-parser'); -const express = require('express'); -const morgan = require('morgan'); -const redis = require('redis'); -const fetch = require('node-fetch-v2'); -const port = require('../../../test_util/app-port')(); - -const cls = require('../../../../../core/src/tracing/cls'); -const app = express(); -const logPrefix = `Redis Latest App (${process.pid}):\t`; -let connectedToRedis = false; -const agentPort = process.env.INSTANA_AGENT_PORT; - -const client = redis.createClient({ url: `redis://${process.env.REDIS}` }); -const client2 = redis.createClient({ url: `redis://${process.env.REDIS_ALTERNATIVE}` }); - -[client, client2].forEach(c => { - c.on('error', err => log('Redis Client Error', err)); -}); - -(async () => { - await client.connect(); - await client2.connect(); - connectedToRedis = true; - log(`Connected to redis client (version: ${process.env.REDIS_VERSION}).`); -})(); - -if (process.env.WITH_STDOUT) { - app.use(morgan(`${logPrefix}:method :url :status`)); -} - -app.use(bodyParser.json()); - -app.get('/', (req, res) => { - if (!connectedToRedis) { - res.sendStatus(500); - } else { - res.sendStatus(200); - } -}); - -app.post('/clearkeys', async (req, res) => { - cls.isTracing() && cls.setTracingLevel('0'); - await client.flushAll(); - - cls.isTracing() && cls.setTracingLevel('1'); - res.sendStatus(200); -}); - -app.post('/values', async (req, res) => { - const key = req.query.key; - const value = req.query.value; - - try { - await client.set(key, value); - log('Set key successfully.'); - } catch (e) { - log('Set with key %s, value %s failed', key, value, e); - res.sendStatus(500); - } - await fetch(`http://127.0.0.1:${agentPort}`); - log('Sent agent fetch successfully.'); - res.sendStatus(200); -}); - -app.get('/values', async (req, res) => { - const key = req.query.key; - - try { - const redisRes = await client.get(key); - log('Got redis key successfully.'); - await fetch(`http://127.0.0.1:${agentPort}`); - log('Sent agent request successfully.'); - res.send(redisRes); - } catch (err) { - log('Get with key %s failed', key, err); - res.sendStatus(500); - } -}); - -app.get('/hvals', async (req, res) => { - await client.hVals('key1'); - await fetch(`http://127.0.0.1:${agentPort}`); - log('Sent agent request successfully.'); - res.sendStatus(200); -}); - -app.get('/blocking', async (req, res) => { - const blPopPromise = client.blPop(redis.commandOptions({ isolated: true }), 'mykey', 0); - - try { - await client.lPush('mykey', ['1', '2']); - await blPopPromise; // '2' - - await fetch(`http://127.0.0.1:${agentPort}`); - log('Sent agent request successfully.'); - - res.sendStatus(200); - } catch (err) { - log('Unexpected err', err); - res.sendStatus(500); - } -}); - -app.get('/scan-iterator', async (req, res) => { - // eslint-disable-next-line no-restricted-syntax - for await (const key of client.scanIterator()) { - try { - await client.get(key); - } catch (getErr) { - // ignore for now - } - } - - await fetch(`http://127.0.0.1:${agentPort}`); - log('Sent agent request successfully.'); - - res.sendStatus(200); -}); - -app.get('/hset-hget', async (req, res) => { - await client.hSet('someCollection1', 'key1', 'value1'); - // HGETALL = hGetAll internally, no need to add test coverage for both - const result = await client.hGetAll('someCollection1'); - await fetch(`http://127.0.0.1:${agentPort}`); - res.status(200).send(result.key1); -}); - -app.get('/get-without-waiting', (req, res) => { - const key = req.query.key; - client.get(key); - fetch(`http://127.0.0.1:${agentPort}`).then(() => { - res.sendStatus(200); - }); -}); - -app.get('/set-without-waiting', (req, res) => { - const key = req.query.key; - const value = req.query.value; - - client.set(key, value); - - fetch(`http://127.0.0.1:${agentPort}`).then(() => { - res.sendStatus(200); - }); -}); - -app.get('/failure', async (req, res) => { - try { - // simulating wrong get usage - const redisRes = await client.get(null); - res.send(redisRes); - } catch (err) { - await fetch(`http://127.0.0.1:${agentPort}`); - res.sendStatus(500); - } -}); - -app.get('/multi', async (req, res) => { - try { - await client.multi().set('key', 'value').get('key').exec(); - await fetch(`http://127.0.0.1:${agentPort}`); - res.sendStatus(200); - } catch (err) { - log('Multi failed', err); - res.sendStatus(500); - } -}); - -app.get('/multi-no-waiting', async (req, res) => { - try { - client.multi().set('key', 'value').get('key').exec(); - await fetch(`http://127.0.0.1:${agentPort}`); - res.sendStatus(200); - } catch (err) { - log('Multi failed', err); - res.sendStatus(500); - } -}); - -app.get('/multiFailure', async (req, res) => { - // simulating wrong get usage - try { - await client.multi().set('key', 'value').get(null).exec(); - res.sendStatus(500); - } catch (err) { - log('Multi expected to fail', err); - await fetch(`http://127.0.0.1:${agentPort}`); - res.sendStatus(200); - } -}); - -app.get('/batchSuccess', async (req, res) => { - await client.multi().get('1').set('2', '2').execAsPipeline(); - await fetch(`http://127.0.0.1:${agentPort}`); - res.sendStatus(200); -}); - -app.get('/batchFailure', async (req, res) => { - try { - await client.multi().get(null).set('2', '2').execAsPipeline(); - res.sendStatus(500); - } catch (err) { - log('Batch expected to fail', err); - await fetch(`http://127.0.0.1:${agentPort}`); - res.sendStatus(200); - } -}); - -app.get('/callSequence', async (req, res) => { - const key = 'foo'; - const value = 'bar'; - - try { - await client.set(key, value); - const result = await client.get(key); - await fetch(`http://127.0.0.1:${agentPort}`); - res.send(result); - } catch (err) { - log('Set/Get with key %s, value %s failed', key, value, err); - res.sendStatus(500); - } -}); - -app.post('/two-different-target-hosts', async (req, res) => { - try { - const response = {}; - response.response1 = await client.set(req.query.key, req.query.value1); - response.response2 = await client2.set(req.query.key, req.query.value2); - res.json(response); - } catch (e) { - log('Redis set operation failed.', e); - res.sendStatus(500); - } -}); - -app.listen(port, () => { - log(`Listening on port: ${port}`); -}); - -function log() { - const args = Array.prototype.slice.call(arguments); - args[0] = logPrefix + args[0]; - console.log.apply(console, args); -} diff --git a/packages/collector/test/tracing/database/redis/legacyApp.js b/packages/collector/test/tracing/database/redis/legacyApp.js index 2eb4630f88..24cc1e208f 100644 --- a/packages/collector/test/tracing/database/redis/legacyApp.js +++ b/packages/collector/test/tracing/database/redis/legacyApp.js @@ -13,6 +13,8 @@ process.on('SIGTERM', () => { process.exit(0); }); +require('./mockVersion'); + require('../../../..')(); const bodyParser = require('body-parser'); diff --git a/packages/collector/test/tracing/database/redis/test.js b/packages/collector/test/tracing/database/redis/test.js index 3a34964587..671a6665c8 100644 --- a/packages/collector/test/tracing/database/redis/test.js +++ b/packages/collector/test/tracing/database/redis/test.js @@ -6,6 +6,7 @@ 'use strict'; const expect = require('chai').expect; +const path = require('path'); const constants = require('@instana/core').tracing.constants; const supportedVersion = require('@instana/core').tracing.supportedVersion; const config = require('../../../../../core/test/config'); @@ -21,748 +22,794 @@ const { const ProcessControls = require('../../../test_util/ProcessControls'); const globalAgent = require('../../../globalAgent'); -describe('tracing/redis', function () { - this.timeout(config.getTestTimeout()); - - const agentControls = globalAgent.instance; - - ['latest', 'v3'].forEach(redisVersion => { - const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : describe.skip; - - mochaSuiteFn(`redis@${redisVersion}`, function () { - globalAgent.setUpCleanUpHooks(); - let controls; - - before(async () => { - controls = new ProcessControls({ - dirname: __dirname, - useGlobalAgent: true, - env: { - REDIS_VERSION: redisVersion +// Please run this command on the root folder to start the redis instance: +// node bin/start-test-containers.js --redis +// +// Please set the environment variables to run the tests against azure redis cluster: +// export AZURE_REDIS_CLUSTER=team-nodejs-redis-cluster-tekton.redis.cache.windows.net:6380 +// export AZURE_REDIS_CLUSTER_PWD= +['default', 'cluster'].forEach(setupType => { + describe(`tracing/redis ${setupType}`, function () { + ['redis', '@redis/client'].forEach(redisPkg => { + describe(`require: ${redisPkg}`, function () { + this.timeout(config.getTestTimeout() * 4); + const agentControls = globalAgent.instance; + + ['latest', 'v3'].forEach(redisVersion => { + let mochaSuiteFn = supportedVersion(process.versions.node) ? describe : describe.skip; + + // NOTE: clustering was added in v4 + // https://github.com/redis/node-redis/blob/master/CHANGELOG.md#v400---24-nov-2021 + if (redisVersion !== 'latest' && setupType === 'cluster') { + mochaSuiteFn = describe.skip; } - }); - - await controls.startAndWaitForAgentConnection(); - }); - - beforeEach(async () => { - await agentControls.clearReceivedTraceData(); - }); - - before(async () => { - await controls.sendRequest({ - method: 'POST', - path: '/clearkeys' - }); - }); - after(async () => { - await controls.stop(); - }); - - afterEach(async () => { - await controls.clearIpcMessages(); - }); + // NOTE: redis v3 does not support using @redis/client + if (redisVersion !== 'latest' && redisPkg === '@redis/client') { + mochaSuiteFn = describe.skip; + } - it('must trace set/get calls', () => - controls - .sendRequest({ - method: 'POST', - path: '/values', - qs: { - key: 'price', - value: 42 - } - }) - .then(() => - controls.sendRequest({ - method: 'GET', - path: '/values', - qs: { - key: 'price' - } - }) - ) - .then(response => { - expect(String(response)).to.equal('42'); - - return retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('POST') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('set') - ]); - - const readEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET'), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(readEntrySpan.t), - span => expect(span.p).to.equal(readEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('get') - ]); - - verifyHttpExit(spans, writeEntrySpan); - verifyHttpExit(spans, readEntrySpan); - }) - ); - })); - - it('must trace hset/hget calls', () => - controls - .sendRequest({ - method: 'GET', - path: '/hset-hget' - }) - .then(response => { - expect(String(response)).to.equal('value1'); - - return retry(() => - agentControls.getSpans().then(spans => { - const entrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(entrySpan.t), - span => expect(span.p).to.equal(entrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => - redisVersion === 'latest' - ? expect(span.data.redis.command).to.equal('hSet') - : expect(span.data.redis.command).to.equal('hset') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(entrySpan.t), - span => expect(span.p).to.equal(entrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => - redisVersion === 'latest' - ? expect(span.data.redis.command).to.equal('hGetAll') - : expect(span.data.redis.command).to.equal('hget') - ]); - - verifyHttpExit(spans, entrySpan); - }) - ); - })); - - it('must not trace get without waiting', () => - controls - .sendRequest({ - method: 'GET', - path: '/get-without-waiting', - qs: { - key: 'price' - } - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace set without cb', () => - controls - .sendRequest({ - method: 'GET', - path: '/set-without-waiting', - qs: { - key: 'price', - value: 42 + mochaSuiteFn(`redis@${redisVersion}`, function () { + globalAgent.setUpCleanUpHooks(); + let controls; + + before(async () => { + controls = new ProcessControls({ + useGlobalAgent: true, + appPath: + redisVersion === 'latest' ? path.join(__dirname, 'app.js') : path.join(__dirname, 'legacyApp.js'), + env: { + REDIS_VERSION: redisVersion, + REDIS_PKG: redisPkg, + REDIS_CLUSTER: setupType === 'cluster' + } + }); + + await controls.startAndWaitForAgentConnection(5000, Date.now() + 1000 * 60 * 5); + }); + + beforeEach(async () => { + await agentControls.clearReceivedTraceData(); + }); + + before(async () => { + await controls.sendRequest({ + method: 'POST', + path: '/clearkeys' + }); + }); + + after(async () => { + await controls.stop(); + }); + + afterEach(async () => { + await controls.clearIpcMessages(); + }); + + it('must trace set/get calls', () => + controls + .sendRequest({ + method: 'POST', + path: '/values', + qs: { + key: 'price', + value: 42 + } + }) + .then(() => + controls.sendRequest({ + method: 'GET', + path: '/values', + qs: { + key: 'price' + } + }) + ) + .then(response => { + expect(String(response)).to.equal('42'); + + return retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('POST') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('set') + ]); + + const readEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET'), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(readEntrySpan.t), + span => expect(span.p).to.equal(readEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('get') + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + verifyHttpExit(controls, spans, readEntrySpan); + + // 2 x entry span + // 2 x redis exit span + // 2 x http exit span + expect(spans.length).to.eql(6); + }) + ); + })); + + it('must trace hset/hget calls', () => + controls + .sendRequest({ + method: 'GET', + path: '/hset-hget' + }) + .then(response => { + expect(String(response)).to.equal('value1'); + + return retry(() => + agentControls.getSpans().then(spans => { + const entrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(entrySpan.t), + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => + redisVersion === 'latest' + ? expect(span.data.redis.command).to.equal('hSet') + : expect(span.data.redis.command).to.equal('hset') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(entrySpan.t), + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => + redisVersion === 'latest' + ? expect(span.data.redis.command).to.equal('hGetAll') + : expect(span.data.redis.command).to.equal('hget') + ]); + + verifyHttpExit(controls, spans, entrySpan); + }) + ); + })); + + it('must not trace get without waiting', () => + controls + .sendRequest({ + method: 'GET', + path: '/get-without-waiting', + qs: { + key: 'price' + } + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace set without cb', () => + controls + .sendRequest({ + method: 'GET', + path: '/set-without-waiting', + qs: { + key: 'price', + value: 42 + } + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('set') + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace failed redis calls', () => + controls + .sendRequest({ + method: 'GET', + path: '/failure' + }) + .catch(() => { + // ignore errors + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(1), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('get'), + span => expect(span.data.redis.error).to.be.a('string') + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace multi calls', () => + controls + .sendRequest({ + method: 'GET', + path: '/multi' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + // contains two batched span + span => expect(span.b).to.be.an('object'), + span => expect(span.b.s).to.equal(2), + span => expect(span.b.u).to.not.exist, + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('multi'), + span => + redisVersion === 'latest' + ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) + : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + if (redisVersion !== 'latest') { + it('must trace multi calls with sub callbacks', () => + controls + .sendRequest({ + method: 'GET', + path: '/multi-sub-cb' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(1), + // contains two batched span + span => expect(span.b).to.be.an('object'), + span => expect(span.b.s).to.equal(2), + span => expect(span.b.u).to.not.exist, + span => verifyConnection(setupType, span), + span => + redisVersion === 'v3' + ? expect(span.data.redis.error).to.contain( + "ERR wrong number of arguments for 'hget' command" + ) + : expect(span.data.redis.error).to.contain( + 'EXECABORT Transaction discarded because of previous errors.' + ), + span => expect(span.data.redis.command).to.equal('multi'), + span => + redisVersion === 'latest' + ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) + : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); } - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('set') - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace failed redis calls', () => - controls - .sendRequest({ - method: 'GET', - path: '/failure' - }) - .catch(() => { - // ignore errors - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(1), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('get'), - span => expect(span.data.redis.error).to.be.a('string') - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace multi calls', () => - controls - .sendRequest({ - method: 'GET', - path: '/multi' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - // contains two batched span - span => expect(span.b).to.be.an('object'), - span => expect(span.b.s).to.equal(2), - span => expect(span.b.u).to.not.exist, - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('multi'), - span => - redisVersion === 'latest' - ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) - : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - if (redisVersion !== 'latest') { - it('must trace multi calls with sub callbacks', () => - controls - .sendRequest({ - method: 'GET', - path: '/multi-sub-cb' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(1), - // contains two batched span - span => expect(span.b).to.be.an('object'), - span => expect(span.b.s).to.equal(2), - span => expect(span.b.u).to.not.exist, - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => - redisVersion === 'v3' - ? expect(span.data.redis.error).to.contain("ERR wrong number of arguments for 'hget' command") - : expect(span.data.redis.error).to.contain( - 'EXECABORT Transaction discarded because of previous errors.' - ), - span => expect(span.data.redis.command).to.equal('multi'), - span => - redisVersion === 'latest' - ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) - : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) - ]); - - verifyHttpExit(spans, writeEntrySpan); + it('must trace multi calls without exec waiting', () => + controls + .sendRequest({ + method: 'GET', + path: '/multi-no-waiting' }) - ) - )); - } - - it('must trace multi calls without exec waiting', () => - controls - .sendRequest({ - method: 'GET', - path: '/multi-no-waiting' - }) - .catch(() => { - // ignore errors - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.b).to.be.an('object'), - span => expect(span.b.s).to.equal(2), - span => expect(span.b.u).to.not.exist, - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('multi'), - span => - redisVersion === 'latest' - ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) - : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace failed multi calls', () => - controls - .sendRequest({ - method: 'GET', - path: '/multiFailure' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(1), - span => expect(span.b).to.be.an('object'), - span => expect(span.b.s).to.equal(2), - span => expect(span.b.u).to.not.exist, - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('multi'), - span => - redisVersion === 'latest' - ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) - : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace batch calls', () => - controls - .sendRequest({ - method: 'GET', - path: '/batchSuccess' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.b).to.be.an('object'), - span => expect(span.b.s).to.equal(2), - span => expect(span.b.u).to.not.exist, - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('pipeline'), - span => - redisVersion === 'latest' - ? expect(span.data.redis.subCommands).to.deep.equal(['GET', 'SET']) - : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace failed batch calls', () => - controls - .sendRequest({ - method: 'GET', - path: '/batchFailure' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(1), - span => expect(span.b).to.be.an('object'), - span => expect(span.b.s).to.equal(2), - span => expect(span.b.u).to.not.exist, - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('pipeline'), - span => - redisVersion === 'latest' - ? expect(span.data.redis.subCommands).to.deep.equal(['GET', 'SET']) - : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - it('must trace call sequences', () => - controls - .sendRequest({ - method: 'GET', - path: '/callSequence' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const writeEntrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.data.redis.command).to.equal('set'), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid') - ]); - - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(writeEntrySpan.t), - span => expect(span.p).to.equal(writeEntrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.data.redis.command).to.equal('get'), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid') - ]); - - verifyHttpExit(spans, writeEntrySpan); - }) - ) - )); - - if (redisVersion === 'latest') { - it('must trace hvals', () => - controls - .sendRequest({ - method: 'GET', - path: '/hvals' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const entrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); + .catch(() => { + // ignore errors + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => expect(span.b).to.be.an('object'), + span => expect(span.b.s).to.equal(2), + span => expect(span.b.u).to.not.exist, + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('multi'), + span => + redisVersion === 'latest' + ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) + : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace failed multi calls', () => + controls + .sendRequest({ + method: 'GET', + path: '/multiFailure' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(1), + span => expect(span.b).to.be.an('object'), + span => expect(span.b.s).to.equal(2), + span => expect(span.b.u).to.not.exist, + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('multi'), + span => + redisVersion === 'latest' + ? expect(span.data.redis.subCommands).to.deep.equal(['SET', 'GET']) + : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace batch calls', () => + controls + .sendRequest({ + method: 'GET', + path: '/batchSuccess' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => expect(span.b).to.be.an('object'), + span => expect(span.b.s).to.equal(2), + span => expect(span.b.u).to.not.exist, + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('pipeline'), + span => + redisVersion === 'latest' + ? expect(span.data.redis.subCommands).to.deep.equal(['GET', 'SET']) + : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace failed batch calls', () => + controls + .sendRequest({ + method: 'GET', + path: '/batchFailure' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(1), + span => expect(span.b).to.be.an('object'), + span => expect(span.b.s).to.equal(2), + span => expect(span.b.u).to.not.exist, + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('pipeline'), + span => + redisVersion === 'latest' + ? expect(span.data.redis.subCommands).to.deep.equal(['GET', 'SET']) + : expect(span.data.redis.subCommands).to.deep.equal(['hset', 'hget']) + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + it('must trace call sequences', () => + controls + .sendRequest({ + method: 'GET', + path: '/callSequence' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const writeEntrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.data.redis.command).to.equal('set'), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(writeEntrySpan.t), + span => expect(span.p).to.equal(writeEntrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.data.redis.command).to.equal('get'), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid') + ]); + + verifyHttpExit(controls, spans, writeEntrySpan); + }) + ) + )); + + if (redisVersion === 'latest') { + it('must trace hvals', () => + controls + .sendRequest({ + method: 'GET', + path: '/hvals' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const entrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(entrySpan.t), + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('hVals') + ]); + + verifyHttpExit(controls, spans, entrySpan); + }) + ) + )); + + // scanIterator not available on cluster. + if (setupType !== 'cluster') { + it('must trace scan iterator usage', () => + controls + .sendRequest({ + method: 'GET', + path: '/scan-iterator' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const entrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectExactlyNMatching(spans, 4, [ + span => expect(span.t).to.equal(entrySpan.t), + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('get') + ]); + + verifyHttpExit(controls, spans, entrySpan); + }) + ) + )); + } - expectAtLeastOneMatching(spans, [ - span => expect(span.t).to.equal(entrySpan.t), - span => expect(span.p).to.equal(entrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('hVals') - ]); + // See https://redis.js.org/#node-redis-usage-basic-example blocking commands + it('blocking', () => + controls + .sendRequest({ + method: 'GET', + path: '/blocking' + }) + .then(() => + retry(() => + agentControls.getSpans().then(spans => { + const entrySpan = expectAtLeastOneMatching(spans, [ + span => expect(span.n).to.equal('node.http.server'), + span => expect(span.data.http.method).to.equal('GET') + ]); + + expectAtLeastOneMatching(spans, [ + span => expect(span.t).to.equal(entrySpan.t), + span => expect(span.p).to.equal(entrySpan.s), + span => expect(span.n).to.equal('redis'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => verifyConnection(setupType, span), + span => expect(span.data.redis.command).to.equal('blPop') + ]); + + verifyHttpExit(controls, spans, entrySpan); + }) + ) + )); + } - verifyHttpExit(spans, entrySpan); - }) - ) - )); - - it('must trace scan iterator usage', () => - controls - .sendRequest({ - method: 'GET', - path: '/scan-iterator' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { + it('[suppressed] should not trace', async function () { + await controls.sendRequest({ + method: 'POST', + path: '/values', + qs: { + key: 'price', + value: 42 + }, + suppressTracing: true + }); + + await delay(1000); + const spans = await agentControls.getSpans(); + if (spans.length > 0) { + expect.fail(`Unexpected spans: ${stringifyItems(spans)}`); + } + }); + + it('[suppressed] should not trace multi', async function () { + await controls.sendRequest({ + method: 'GET', + path: '/multi', + suppressTracing: true + }); + + await delay(1000); + const spans = await agentControls.getSpans(); + if (spans.length > 0) { + expect.fail(`Unexpected spans: ${stringifyItems(spans)}`); + } + }); + + // Does not make sense for cluster. + if (setupType !== 'cluster') { + it('call two different hosts', async () => { + const response = await controls.sendRequest({ + method: 'POST', + path: '/two-different-target-hosts', + qs: { + key: 'key', + value1: 'value1', + value2: 'value2' + } + }); + + expect(response.response1).to.equal('OK'); + expect(response.response2).to.equal('OK'); + + await retry(async () => { + const spans = await agentControls.getSpans(); const entrySpan = expectAtLeastOneMatching(spans, [ span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') + span => expect(span.data.http.method).to.equal('POST') ]); - - expectExactlyNMatching(spans, 4, [ + expectExactlyOneMatching(spans, [ span => expect(span.t).to.equal(entrySpan.t), span => expect(span.p).to.equal(entrySpan.s), span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('get') + span => expect(span.data.redis.command).to.equal('set'), + span => expect(span.data.redis.connection).to.contain('127.0.0.1') ]); - - verifyHttpExit(spans, entrySpan); - }) - ) - )); - - // See https://redis.js.org/#node-redis-usage-basic-example blocking commands - it('blocking', () => - controls - .sendRequest({ - method: 'GET', - path: '/blocking' - }) - .then(() => - retry(() => - agentControls.getSpans().then(spans => { - const entrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('GET') - ]); - - expectAtLeastOneMatching(spans, [ + expectExactlyOneMatching(spans, [ span => expect(span.t).to.equal(entrySpan.t), span => expect(span.p).to.equal(entrySpan.s), span => expect(span.n).to.equal('redis'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.redis.connection).to.contain(process.env.REDIS), - span => expect(span.data.redis.command).to.equal('blPop') + span => expect(span.data.redis.command).to.equal('set'), + span => expect(span.data.redis.connection).to.contain('localhost') ]); - - verifyHttpExit(spans, entrySpan); - }) - ) - )); - } - - it('[suppressed] should not trace', async function () { - await controls.sendRequest({ - method: 'POST', - path: '/values', - qs: { - key: 'price', - value: 42 - }, - suppressTracing: true - }); - - await delay(1000); - const spans = await agentControls.getSpans(); - if (spans.length > 0) { - expect.fail(`Unexpected spans: ${stringifyItems(spans)}`); - } - }); - - it('[suppressed] should not trace multi', async function () { - await controls.sendRequest({ - method: 'GET', - path: '/multi', - suppressTracing: true + }); + }); + } + }); }); - await delay(1000); - const spans = await agentControls.getSpans(); - if (spans.length > 0) { - expect.fail(`Unexpected spans: ${stringifyItems(spans)}`); + function verifyHttpExit(controls, spans, parent) { + expectExactlyOneMatching(spans, [ + span => expect(span.t).to.equal(parent.t), + span => expect(span.p).to.equal(parent.s), + span => expect(span.n).to.equal('node.http.client'), + span => expect(span.k).to.equal(constants.EXIT), + span => expect(span.f.e).to.equal(String(controls.getPid())), + span => expect(span.f.h).to.equal('agent-stub-uuid'), + span => expect(span.async).to.not.exist, + span => expect(span.error).to.not.exist, + span => expect(span.ec).to.equal(0), + span => expect(span.data.http.method).to.equal('GET'), + span => expect(span.data.http.url).to.match(/http:\/\/127\.0\.0\.1:/), + span => expect(span.data.http.status).to.equal(200) + ]); } - }); - it('call two different hosts', async () => { - const response = await controls.sendRequest({ - method: 'POST', - path: '/two-different-target-hosts', - qs: { - key: 'key', - value1: 'value1', - value2: 'value2' + function verifyConnection(type, span) { + if (type === 'cluster') { + expect(span.data.redis.connection).to.contain(process.env.AZURE_REDIS_CLUSTER); + } else { + expect(span.data.redis.connection).to.contain(process.env.REDIS); } - }); - - expect(response.response1).to.equal('OK'); - expect(response.response2).to.equal('OK'); - - await retry(async () => { - const spans = await agentControls.getSpans(); - const entrySpan = expectAtLeastOneMatching(spans, [ - span => expect(span.n).to.equal('node.http.server'), - span => expect(span.data.http.method).to.equal('POST') - ]); - expectExactlyOneMatching(spans, [ - span => expect(span.t).to.equal(entrySpan.t), - span => expect(span.p).to.equal(entrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.data.redis.command).to.equal('set'), - span => expect(span.data.redis.connection).to.contain('127.0.0.1') - ]); - expectExactlyOneMatching(spans, [ - span => expect(span.t).to.equal(entrySpan.t), - span => expect(span.p).to.equal(entrySpan.s), - span => expect(span.n).to.equal('redis'), - span => expect(span.data.redis.command).to.equal('set'), - span => expect(span.data.redis.connection).to.contain('localhost') - ]); - }); + } }); - - function verifyHttpExit(spans, parent) { - expectExactlyOneMatching(spans, [ - span => expect(span.t).to.equal(parent.t), - span => expect(span.p).to.equal(parent.s), - span => expect(span.n).to.equal('node.http.client'), - span => expect(span.k).to.equal(constants.EXIT), - span => expect(span.f.e).to.equal(String(controls.getPid())), - span => expect(span.f.h).to.equal('agent-stub-uuid'), - span => expect(span.async).to.not.exist, - span => expect(span.error).to.not.exist, - span => expect(span.ec).to.equal(0), - span => expect(span.data.http.method).to.equal('GET'), - span => expect(span.data.http.url).to.match(/http:\/\/127\.0\.0\.1:/), - span => expect(span.data.http.status).to.equal(200) - ]); - } }); }); }); diff --git a/packages/core/src/tracing/instrumentation/database/redis.js b/packages/core/src/tracing/instrumentation/database/redis.js index 31c42bbdf7..ec59f771a9 100644 --- a/packages/core/src/tracing/instrumentation/database/redis.js +++ b/packages/core/src/tracing/instrumentation/database/redis.js @@ -28,7 +28,9 @@ exports.deactivate = function deactivate() { exports.init = function init() { // v4 commands, "redis-commands" is outdated and no longer compatible with it hook.onFileLoad(/\/@redis\/client\/dist\/lib\/cluster\/commands.js/, captureCommands); + hook.onModuleLoad('redis', instrument); + hook.onModuleLoad('@redis/client', instrument); }; let redisCommandList = []; @@ -42,6 +44,75 @@ function instrument(redis) { // NOTE: v4 no longer exposes the RedisClient. We need to wait till `createClient` get's called // to get the instance of the redis client if (!redis.RedisClient) { + const wrapMulti = (addressUrl, isCluster) => { + return function innerWrapMulti(originalMultiFn) { + return function instrumentedMultiInstana() { + const result = originalMultiFn.apply(this, arguments); + const selfMadeQueue = []; + + // batch + const wrapExecAsPipeline = execAsPipelineOriginalFn => { + return function instrumentedExecAsPipelineInstana() { + return instrumentMultiExec( + this, + arguments, + execAsPipelineOriginalFn, + addressUrl, + false, + false, + selfMadeQueue + ); + }; + }; + + // multi + const wrapExec = execOriginalFn => { + return function instrumentedExecAsPipelineInstana() { + return instrumentMultiExec(this, arguments, execOriginalFn, addressUrl, true, false, selfMadeQueue); + }; + }; + + const wrapAddCommand = addCommandOriginalFn => { + return function instrumentedAddCommandInstana() { + if (isCluster) { + selfMadeQueue.push(arguments[1]); + } else { + selfMadeQueue.push(arguments[0]); + } + + return addCommandOriginalFn.apply(this, arguments); + }; + }; + + // NOTE: addCommand will fill our self made queue to know how many + // operations landed in this multi transaction. We are unable to access + // redis internal queue anymore. + shimmer.wrap(result, 'addCommand', wrapAddCommand); + shimmer.wrap(result, 'exec', wrapExec); + + // `execAsPipeline` can be used to trigger batches in 4.x + shimmer.wrap(result, 'execAsPipeline', wrapExecAsPipeline); + + return result; + }; + }; + }; + + const createClusterWrap = originalCreateClusterFn => { + return function instrumentedCreateClusterInstana(createClusterOpts) { + const redisCluster = originalCreateClusterFn.apply(this, arguments); + const addressUrl = createClusterOpts.rootNodes.map(node => node.url).join(', '); + + shimAllCommands(redisCluster, addressUrl, false, redisCommandList); + + if (redisCluster.multi) { + shimmer.wrap(redisCluster, 'multi', wrapMulti(addressUrl, true)); + } + + return redisCluster; + }; + }; + const createClientWrap = originalCreatedClientFn => { return function instrumentedCreateClientInstana(createClientOpts) { const redisClient = originalCreatedClientFn.apply(this, arguments); @@ -68,60 +139,14 @@ function instrument(redis) { shimAllCommands(redisClient, addressUrl, false, redisCommandList); if (redisClient.multi) { - const wrapMulti = originalMultiFn => { - return function instrumentedMultiInstana() { - const result = originalMultiFn.apply(this, arguments); - const selfMadeQueue = []; - - // batch - const wrapExecAsPipeline = execAsPipelineOriginalFn => { - return function instrumentedExecAsPipelineInstana() { - return instrumentMultiExec( - this, - arguments, - execAsPipelineOriginalFn, - addressUrl, - false, - false, - selfMadeQueue - ); - }; - }; - - // multi - const wrapExec = execOriginalFn => { - return function instrumentedExecAsPipelineInstana() { - return instrumentMultiExec(this, arguments, execOriginalFn, addressUrl, true, false, selfMadeQueue); - }; - }; - - const wrapAddCommand = addCommandOriginalFn => { - return function instrumentedAddCommandInstana() { - selfMadeQueue.push(arguments[0]); - return addCommandOriginalFn.apply(this, arguments); - }; - }; - - // NOTE: addCommand will fill our self made queue to know how many - // operations landed in this multi transaction. We are unable to access - // redis internal queue anymore. - shimmer.wrap(result, 'addCommand', wrapAddCommand); - shimmer.wrap(result, 'exec', wrapExec); - - // `execAsPipeline` can be used to trigger batches in 4.x - shimmer.wrap(result, 'execAsPipeline', wrapExecAsPipeline); - - return result; - }; - }; - - shimmer.wrap(redisClient, 'multi', wrapMulti); + shimmer.wrap(redisClient, 'multi', wrapMulti(addressUrl, false)); } return redisClient; }; }; + shimmer.wrap(redis, 'createCluster', createClusterWrap); shimmer.wrap(redis, 'createClient', createClientWrap); } else { const redisClientProto = redis.RedisClient.prototype; diff --git a/packages/core/src/util/initializedTooLateHeuristic.js b/packages/core/src/util/initializedTooLateHeuristic.js index eb0d84fc29..893e164803 100644 --- a/packages/core/src/util/initializedTooLateHeuristic.js +++ b/packages/core/src/util/initializedTooLateHeuristic.js @@ -29,6 +29,7 @@ let patterns = [ /\/@hapi\/call\/lib\//, /\/@prisma\/client\//, /\/@redis\/client\/dist\/lib\/cluster\/commands.js/, + /\/@redis\/client\/dist\/index.js/, /\/amqplib\/lib\//, /\/aws-sdk\/lib\//, // deliberately not including bunyan because we depend on bunyan ourselves diff --git a/packages/core/test/util/initializedTooLateHeuristic_test.js b/packages/core/test/util/initializedTooLateHeuristic_test.js index 08c81b2ce3..2c320b931e 100644 --- a/packages/core/test/util/initializedTooLateHeuristic_test.js +++ b/packages/core/test/util/initializedTooLateHeuristic_test.js @@ -54,7 +54,7 @@ describe('[UNIT] util.initializedTooLateHeurstic', function () { it('hasBeenInitializedTooLate is true', () => { instrumentedModules.forEach(moduleName => { // eslint-disable-next-line no-console - console.log(`for module ${moduleName}`); + console.log(`### for module ${moduleName}`); initializedTooLateHeurstic.reset();