diff --git a/.github/workflows/loadtest-infra.yml b/.github/workflows/loadtest-infra.yml index 75d4eecd1c..18b8ba1152 100644 --- a/.github/workflows/loadtest-infra.yml +++ b/.github/workflows/loadtest-infra.yml @@ -47,6 +47,24 @@ on: type: string default: 3 required: true + fleet_redis_engine: + description: "Elasticache engine to deploy" + type: choice + options: + - redis + - valkey + default: redis + required: false + fleet_redis_engine_version: + description: "Elasticache engine version (must match the engine, e.g. 7.1 for redis, 8.0 for valkey)" + type: string + default: "7.1" + required: false + fleet_redis_parameter_group_family: + description: "Elasticache parameter group family (must match engine+version, e.g. redis7, valkey7, valkey8)" + type: string + default: "redis7" + required: false terraform_action: description: Dry run only? No "terraform apply" type: choice @@ -79,6 +97,9 @@ env: TF_VAR_database_instance_count: "${{ inputs.fleet_database_instance_count }}" TF_VAR_redis_instance_size: "${{ inputs.fleet_redis_instance_size }}" TF_VAR_redis_instance_count: "${{ inputs.fleet_redis_instance_count }}" + TF_VAR_redis_engine: "${{ inputs.fleet_redis_engine }}" + TF_VAR_redis_engine_version: "${{ inputs.fleet_redis_engine_version }}" + TF_VAR_redis_parameter_group_family: "${{ inputs.fleet_redis_parameter_group_family }}" permissions: id-token: write diff --git a/.github/workflows/test-go-suite.yaml b/.github/workflows/test-go-suite.yaml index ea10886991..51a83c6099 100644 --- a/.github/workflows/test-go-suite.yaml +++ b/.github/workflows/test-go-suite.yaml @@ -14,6 +14,11 @@ on: required: false type: string default: '' + redis: + description: 'Redis/Valkey Docker image (e.g., "redis:8.0", "valkey/valkey:8.0"). Leave empty to use the docker-compose default (redis:6.2).' + required: false + type: string + default: '' cover_pkg: description: 'Go coverage package pattern (e.g., "github.com/fleetdm/fleet/v4/server/activity/..."). Defaults to all fleet packages.' required: false @@ -101,12 +106,19 @@ jobs: - name: Compute artifact prefix run: | + PREFIX="${{ inputs.suite }}" if [[ -n "${{ inputs.mysql }}" ]]; then MYSQL_ID=$(echo "${{ inputs.mysql }}" | tr -d ':') - echo "ARTIFACT_PREFIX=${{ inputs.suite }}-${MYSQL_ID}" >> $GITHUB_ENV - else - echo "ARTIFACT_PREFIX=${{ inputs.suite }}" >> $GITHUB_ENV + PREFIX="${PREFIX}-${MYSQL_ID}" fi + # Append the Redis/Valkey id only when an explicit image is requested, so the + # default callers keep their existing artifact names and matrix legs that vary + # only by Redis image don't collide on artifact upload. + if [[ -n "${{ inputs.redis }}" ]]; then + REDIS_ID=$(echo "${{ inputs.redis }}" | tr -d ':/') + PREFIX="${PREFIX}-${REDIS_ID}" + fi + echo "ARTIFACT_PREFIX=${PREFIX}" >> $GITHUB_ENV - name: Set Go race setting on schedule if: ${{ inputs.is_cron }} @@ -131,7 +143,7 @@ jobs: - name: Start Infra Dependencies if: ${{ env.NEED_DOCKER }} # Use & to background this - run: FLEET_MYSQL_IMAGE=${{ inputs.mysql }} $DOCKER_COMMAND & + run: FLEET_REDIS_IMAGE=${{ inputs.redis }} FLEET_MYSQL_IMAGE=${{ inputs.mysql }} $DOCKER_COMMAND & - name: Add TLS certificate for SMTP Tests if: ${{ env.NEED_DOCKER }} @@ -199,7 +211,7 @@ jobs: docker compose down echo "Restarting containers..." - FLEET_MYSQL_IMAGE=${{ inputs.mysql }} $DOCKER_COMMAND & + FLEET_REDIS_IMAGE=${{ inputs.redis }} FLEET_MYSQL_IMAGE=${{ inputs.mysql }} $DOCKER_COMMAND & # Give containers a moment to start sleep 10 diff --git a/.github/workflows/test-go.yaml b/.github/workflows/test-go.yaml index 0b0d1c6794..4fb6d71bf7 100644 --- a/.github/workflows/test-go.yaml +++ b/.github/workflows/test-go.yaml @@ -105,6 +105,30 @@ jobs: FLEET_RELEASE_GITHUB_PAT: ${{ secrets.FLEET_RELEASE_GITHUB_PAT }} SLACK_G_HELP_ENGINEERING_WEBHOOK_URL: ${{ secrets.SLACK_G_HELP_ENGINEERING_WEBHOOK_URL }} + # ────────────────────────────────────────────────────────────────────────── + # Extended Redis/Valkey coverage: only on the nightly cron schedule. + # Runs the Redis-touching suites against newer Redis versions and Valkey, in + # both standalone and cluster mode. MySQL is pinned to the always-run version + # (single DB dimension, not NxM). Redis 6.2 is already covered by the always-run + # test-go job above, so it's omitted here. + # make sure to update supported versions docs when Redis/Valkey versions change + # ────────────────────────────────────────────────────────────────────────── + test-go-extended-redis: + if: github.event_name == 'schedule' + strategy: + matrix: + suite: ["integration-core", "integration-enterprise", "integration-mdm", "main", "service"] + redis: ["redis:7.4", "redis:8.0", "valkey/valkey:7.2", "valkey/valkey:8.0"] + uses: ./.github/workflows/test-go-suite.yaml + with: + suite: ${{ matrix.suite }} + mysql: "mysql:8.0.44" + redis: ${{ matrix.redis }} + is_cron: true + secrets: + FLEET_RELEASE_GITHUB_PAT: ${{ secrets.FLEET_RELEASE_GITHUB_PAT }} + SLACK_G_HELP_ENGINEERING_WEBHOOK_URL: ${{ secrets.SLACK_G_HELP_ENGINEERING_WEBHOOK_URL }} + # Based on https://github.com/micromdm/nanomdm/blob/main/.github/workflows/on-push-pr.yml#L87 test-go-nanomdm: runs-on: 'ubuntu-latest' @@ -224,7 +248,7 @@ jobs: # We upload all backend coverage in one step so that we're less like to end up in a situation with a partial coverage report. upload-coverage: - needs: [test-go-no-db, test-go, test-go-extended-mysql, test-go-nanomdm] + needs: [test-go-no-db, test-go, test-go-extended-mysql, test-go-extended-redis, test-go-nanomdm] # Run even if extended-mysql was skipped (non-cron) or individual suites failed. if: always() runs-on: ubuntu-latest @@ -251,7 +275,7 @@ jobs: # Our Go test suites are run with continue-on-error: true, so they don't contribute to the workflow pass/fail. # This job explicitly checks if any Go test suites have failed and marks the overall workflow with the proper pass/fail status. aggregate-result: - needs: [test-go-no-db, test-go, test-go-extended-mysql] + needs: [test-go-no-db, test-go, test-go-extended-mysql, test-go-extended-redis] # Run even if extended-mysql was skipped (non-cron) or individual suites failed. if: always() runs-on: ubuntu-latest diff --git a/docker-compose-redis-cluster.yml b/docker-compose-redis-cluster.yml index 8aac4e7fc7..938f24c345 100644 --- a/docker-compose-redis-cluster.yml +++ b/docker-compose-redis-cluster.yml @@ -2,7 +2,7 @@ version: "2" services: redis-cluster-setup: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-cli --cluster create 172.20.0.31:7001 172.20.0.32:7002 172.20.0.33:7003 172.20.0.34:7004 172.20.0.35:7005 172.20.0.36:7006 --cluster-yes --cluster-replicas 1 networks: cluster_network: @@ -16,7 +16,7 @@ services: - redis-cluster-6 redis-cluster-1: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-server /usr/local/etc/redis/redis.conf ports: - "127.0.0.1:7001:7001" @@ -27,7 +27,7 @@ services: ipv4_address: 172.20.0.31 redis-cluster-2: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-server /usr/local/etc/redis/redis.conf ports: - "127.0.0.1:7002:7002" @@ -38,7 +38,7 @@ services: ipv4_address: 172.20.0.32 redis-cluster-3: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-server /usr/local/etc/redis/redis.conf ports: - "127.0.0.1:7003:7003" @@ -49,7 +49,7 @@ services: ipv4_address: 172.20.0.33 redis-cluster-4: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-server /usr/local/etc/redis/redis.conf ports: - "127.0.0.1:7004:7004" @@ -60,7 +60,7 @@ services: ipv4_address: 172.20.0.34 redis-cluster-5: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-server /usr/local/etc/redis/redis.conf ports: - "127.0.0.1:7005:7005" @@ -71,7 +71,7 @@ services: ipv4_address: 172.20.0.35 redis-cluster-6: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} command: redis-server /usr/local/etc/redis/redis.conf ports: - "127.0.0.1:7006:7006" diff --git a/docker-compose.yml b/docker-compose.yml index ed679056e7..daba0e89f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -118,7 +118,7 @@ services: - ServerOptions__TlsCertificatePrivateKey=/certs/fleet.key redis: - image: redis:6.2 + image: ${FLEET_REDIS_IMAGE:-redis:6.2} ports: - "127.0.0.1:${FLEET_REDIS_PORT:-6379}:6379" diff --git a/infrastructure/loadtesting/terraform/infra/README.md b/infrastructure/loadtesting/terraform/infra/README.md index 4f7c23512c..aaa811e20d 100644 --- a/infrastructure/loadtesting/terraform/infra/README.md +++ b/infrastructure/loadtesting/terraform/infra/README.md @@ -80,6 +80,23 @@ terraform apply -var=tag=v4.72.0 -var=enable_otel=true This deploys both Fleet and SigNoz in a single command. See [../signoz/README.md](../signoz/README.md) for architecture details. +## Running against Valkey instead of Redis + +By default the loadtest environment provisions ElastiCache with the Redis engine. To load test +against [Valkey](https://valkey.io/) instead, override the engine, version, and parameter group +family together (all three must match): + +```sh +# Valkey 8.x +terraform apply -var=tag=v4.72.0 -var=redis_engine=valkey -var=redis_engine_version=8.0 -var=redis_parameter_group_family=valkey8 + +# Valkey 7.2 +terraform apply -var=tag=v4.72.0 -var=redis_engine=valkey -var=redis_engine_version=7.2 -var=redis_parameter_group_family=valkey7 +``` + +Fleet connects to ElastiCache the same way regardless of engine (Valkey is wire-compatible with +Redis), so no other variables need to change. + ### Accessing the SigNoz UI After deploying with `enable_otel=true`, get the SigNoz UI URL: @@ -198,8 +215,11 @@ terraform workspace delete | [fleet\_task\_count](#input\_fleet\_task\_count) | The total number (max) that ECS can scale Fleet containers up to | `number` | `5` | no | | [fleet\_task\_cpu](#input\_fleet\_task\_cpu) | The CPU configuration for Fleet containers | `number` | `512` | no | | [fleet\_task\_memory](#input\_fleet\_task\_memory) | The memory configuration for Fleet containers | `number` | `4096` | no | +| [redis\_engine](#input\_redis\_engine) | The Elasticache engine to use: "redis" or "valkey". | `string` | `"redis"` | no | +| [redis\_engine\_version](#input\_redis\_engine\_version) | The Elasticache engine version (e.g. "7.1" for Redis, "8.0" for Valkey). | `string` | `"7.1"` | no | | [redis\_instance\_count](#input\_redis\_instance\_count) | The number of Elasticache nodes | `number` | `3` | no | | [redis\_instance\_size](#input\_redis\_instance\_size) | The instance size for Elasticache nodes | `string` | `"cache.t4g.micro"` | no | +| [redis\_parameter\_group\_family](#input\_redis\_parameter\_group\_family) | The Elasticache parameter group family (e.g. "redis7", "valkey7", "valkey8"). Must match the engine and version. | `string` | `"redis7"` | no | | [tag](#input\_tag) | The tag to deploy. This would be the same as the branch name | `string` | `"v4.76.1"` | no | ## Outputs diff --git a/infrastructure/loadtesting/terraform/infra/main.tf b/infrastructure/loadtesting/terraform/infra/main.tf index a351877125..b1968d817b 100644 --- a/infrastructure/loadtesting/terraform/infra/main.tf +++ b/infrastructure/loadtesting/terraform/infra/main.tf @@ -62,9 +62,9 @@ module "loadtest" { } redis_config = { name = local.customer - engine = "redis" - engine_version = "7.1" - family = "redis7" + engine = var.redis_engine + engine_version = var.redis_engine_version + family = var.redis_parameter_group_family instance_type = var.redis_instance_size cluster_size = var.redis_instance_count subnets = data.terraform_remote_state.shared.outputs.vpc.private_subnets diff --git a/infrastructure/loadtesting/terraform/infra/variables.tf b/infrastructure/loadtesting/terraform/infra/variables.tf index eb5bccbcf9..8cb41a2718 100644 --- a/infrastructure/loadtesting/terraform/infra/variables.tf +++ b/infrastructure/loadtesting/terraform/infra/variables.tf @@ -60,6 +60,39 @@ variable "redis_instance_count" { } } +# Optional Valkey support. Defaults keep Redis 7.1. For Valkey, set all three, e.g.: +# -var=redis_engine=valkey -var=redis_engine_version=8.0 -var=redis_parameter_group_family=valkey8 +variable "redis_engine" { + description = "The Elasticache engine to use: \"redis\" or \"valkey\"." + type = string + default = "redis" + + validation { + condition = contains(["redis", "valkey"], var.redis_engine) + error_message = "var.redis_engine must be either \"redis\" or \"valkey\"." + } +} + +variable "redis_engine_version" { + description = "The Elasticache engine version (e.g. \"7.1\" for Redis, \"8.0\" for Valkey)." + type = string + default = "7.1" +} + +variable "redis_parameter_group_family" { + description = "The Elasticache parameter group family (e.g. \"redis7\", \"valkey7\", \"valkey8\"). Must match the engine and version." + type = string + default = "redis7" + + # Family must match the engine ("redis*" / "valkey*"). Versions aren't pinned here; + # bad version/family pairs fail at apply. Find valid pairs with: + # aws elasticache describe-cache-engine-versions --engine + validation { + condition = startswith(var.redis_parameter_group_family, var.redis_engine) + error_message = "var.redis_parameter_group_family must match var.redis_engine: use a \"redis*\" family (e.g. redis7) for the \"redis\" engine, or a \"valkey*\" family (e.g. valkey7, valkey8) for the \"valkey\" engine." + } +} + variable "enable_otel" { description = "Enable OpenTelemetry tracing with SigNoz instead of Elastic APM" type = bool