Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
66fa458
feat: support Valkey Cluster with sharding
ankitpatisfdc Jan 5, 2026
2716c39
fix: auth for Valkey cluster
ankitpatisfdc Jan 15, 2026
a747392
docs: update `README.md`s & `NOTES.txt` for cluster mode
ankitpatisfdc Jan 21, 2026
bcd6b4b
fix: schema updated with missing values
ankitpatisfdc Jan 24, 2026
58be01c
feat: `Job` instead of startup script
ankitpatisfdc Feb 28, 2026
8542e08
feat: Istio compatibility
ankitpatisfdc Feb 28, 2026
c288a5a
test: functional tests with `kind`
ankitpatisfdc May 1, 2026
e1c3956
test: functional tests for Istio
ankitpatisfdc May 1, 2026
399134c
fix: multiple issues identified in testing
ankitpatisfdc May 1, 2026
cbc3ccb
feat: multiple Valkey clusters in single K8s cluster
ankitpatisfdc May 1, 2026
c57cdb9
feat: Ambient Mesh support
ankitpatisfdc May 2, 2026
f86c288
fix: multiple Ambient Mesh issues identified in testing
ankitpatisfdc May 2, 2026
6491a27
test: include Ambient Mesh in test matrix
ankitpatisfdc May 3, 2026
2d6fa24
feat: `CLUSTER FAILOVER` upon `rollout restart`
ankitpatisfdc May 3, 2026
d010b2c
docs: clarify comment
ankitpatisfdc May 4, 2026
368b11d
bump `appVersion` to `9.1.0`
ankitpatisfdc May 22, 2026
b74c78f
fix: expose annotations & labels on `cluster-init` job
ankitpatisfdc May 22, 2026
517d93b
fix: cleanup `cluster-init` pod after 300s
ankitpatisfdc May 22, 2026
635a1da
fix: allow overriding probe times & thresholds for large dataset loading
ankitpatisfdc May 22, 2026
5a4be22
fix: update stale `nodes.conf`
ankitpatisfdc May 22, 2026
29a7fda
test: stale `nodes.conf` correctly updated
ankitpatisfdc May 22, 2026
58bceef
fix: `livenessProbe` & `readinessProbe` under cluster mode
ankitpatisfdc May 22, 2026
598a082
fix: `nodes.conf` test
ankitpatisfdc May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*.sh
*.lock
dist/
.vscode
temp/
temp/
*.tgz
27 changes: 27 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,30 @@ package:
validate: lint test
@echo "=== All validations passed ==="

# Create the kind cluster and shared fixtures used by the functional suite
functional-setup:
./functional-tests/setup.sh

# Tear down fixtures (pass --cluster to also delete the kind cluster)
functional-teardown *ARGS:
./functional-tests/teardown.sh {{ARGS}}

# Run one scenario against the already-set-up kind cluster, e.g.
# just functional-scenario off off on on sidecar
# tls/auth/shard/rep are on|off; istio is off|sidecar|ambient.
functional-scenario tls auth shard rep istio:
./functional-tests/run-scenario.sh {{tls}} {{auth}} {{shard}} {{rep}} {{istio}}

# Run the full 48-scenario matrix (set FILTER='tls=on istio=ambient' to narrow)
functional-run:
./functional-tests/run-all.sh

# Run the extra (non-matrix) regression scenarios on their own
functional-extras:
./functional-tests/run-extra-scenarios.sh

# Full functional suite: setup + matrix + teardown including cluster
functional-test:
./functional-tests/setup.sh
./functional-tests/run-all.sh
./functional-tests/teardown.sh --cluster
5 changes: 5 additions & 0 deletions functional-tests/kind-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: valkey-functional
nodes:
- role: control-plane
68 changes: 68 additions & 0 deletions functional-tests/lib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Shared helpers for Valkey functional tests.
# Sourced by every script under functional-tests/.

set -euo pipefail

HERE=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
REPO_ROOT=$(cd -- "${HERE}/.." && pwd)
CHART_DIR=${REPO_ROOT}/valkey

CLUSTER_NAME=${VALKEY_KIND_CLUSTER:-valkey-functional}
KUBE_CONTEXT=kind-${CLUSTER_NAME}
NAMESPACE=${VALKEY_FUNCTIONAL_NAMESPACE:-default}
RELEASE=${VALKEY_RELEASE:-valkey}

AUTH_SECRET=valkey-auth
TLS_SECRET=valkey-tls
# Three testbenches, covering every shape of mesh participation:
# valkey-testbench — never gets an Envoy sidecar (istio=off
# scenarios, or when Istio isn't installed at
# all). Opts out of both sidecar injection
# and ambient capture.
# valkey-testbench-injected — sidecar-injected (istio=on, mode=sidecar).
# valkey-testbench-ambient — ambient-enrolled (istio=on, mode=ambient):
# no sidecar, ztunnel captures its traffic so
# it presents the expected SPIFFE identity to
# Valkey pods' AuthorizationPolicy.
TESTBENCH_POD=valkey-testbench
TESTBENCH_POD_INJECTED=valkey-testbench-injected
TESTBENCH_POD_AMBIENT=valkey-testbench-ambient
# Deliberately hostile: spaces, shell metacharacters ($, `, &, !), a backslash,
# and a double-quote. Every auth=on scenario then exercises both layers of
# quoting on the chart side:
# - the init container's ACL hash pipe (printf %s | sha256sum)
# - the masterauth line in valkey.conf (must be quoted+escaped)
# - the cluster-init Job's REDISCLI_AUTH path
# - the helm-test pod's `cat /valkey-auth/...-password | xargs valkey-cli -a`
# Keeping these in one place means every future auth=on scenario inherits the
# coverage for free.
AUTH_PASSWORD='p@ss w/ spaces & $chars `backticks` "quoted" \backslash'

ISTIO_NAMESPACE=istio-system

log() { printf '=== %s ===\n' "$*"; }

kctl() { kubectl --context="${KUBE_CONTEXT}" --namespace="${NAMESPACE}" "$@"; }
hctl() { helm --kube-context="${KUBE_CONTEXT}" --namespace="${NAMESPACE}" "$@"; }

# kubectl exec into a testbench. First arg is the pod name; rest is the command.
testbench_exec_in() {
local pod=$1; shift
kctl exec "${pod}" -c "${pod}" -- "$@"
}

wait_for_testbench() {
local pod=$1
kctl wait --for=condition=Ready "pod/${pod}" --timeout=180s
}

istio_installed() {
kubectl --context="${KUBE_CONTEXT}" get namespace "${ISTIO_NAMESPACE}" >/dev/null 2>&1
}

# Whether the cluster has Istio's ambient data plane (ztunnel DaemonSet)
# installed. Scenarios that require ambient exit-skip if this returns false.
istio_ambient_installed() {
kubectl --context="${KUBE_CONTEXT}" -n "${ISTIO_NAMESPACE}" \
get daemonset ztunnel >/dev/null 2>&1
}
105 changes: 105 additions & 0 deletions functional-tests/run-all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# Drive every scenario in the matrix, sequentially. Assumes `setup.sh`
# has already created the kind cluster and fixtures.

HERE=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=lib.sh
. "${HERE}/lib.sh"

# 48 scenarios: every combination of tls/auth/shard/rep × istio=
# off|sidecar|ambient. The istio dimension is three-valued rather than two
# because sidecar and ambient share almost nothing below the chart-owned
# templates — different label paths, different mTLS enforcement points
# (Envoy vs ztunnel), different rendered resources (DestinationRule only
# in sidecar; AuthorizationPolicy in both but enforced differently). Keep
# them both in the matrix so a regression in one mode can't hide behind a
# passing result in the other.
SCENARIOS=()
for istio in off sidecar ambient; do
for tls in off on; do
for auth in off on; do
for shard in off on; do
for rep in off on; do
SCENARIOS+=("${tls} ${auth} ${shard} ${rep} ${istio}")
done
done
done
done
done

# Optional filter: `FILTER='tls=on istio=ambient'` runs only matching
# scenarios. Filter values for `istio` are off|sidecar|ambient; `on` is
# accepted as an alias for "sidecar or ambient" to keep old habits working.
matches() {
local tls=$1 auth=$2 shard=$3 rep=$4 istio=$5
for sel in ${FILTER:-}; do
local k=${sel%=*} v=${sel#*=}
local have
case "${k}" in
tls) have=${tls} ;;
auth) have=${auth} ;;
shard) have=${shard} ;;
rep) have=${rep} ;;
istio)
if [[ ${v} == on ]]; then
[[ ${istio} == sidecar || ${istio} == ambient ]] || return 1
continue
fi
have=${istio}
;;
*) echo "bad filter key: ${k}" >&2; exit 2 ;;
esac
[[ ${have} == "${v}" ]] || return 1
done
return 0
}

passed=0
failed=0
skipped=0
failures=()

for s in "${SCENARIOS[@]}"; do
# shellcheck disable=SC2086
read -r tls auth shard rep istio <<<"${s}"
if ! matches "${tls}" "${auth}" "${shard}" "${rep}" "${istio}"; then
continue
fi

# Ambient scenarios require ztunnel to be installed. setup.sh now
# installs the ambient profile by default, but a user running against
# a pre-existing cluster might have only the sidecar data plane —
# skip rather than fail in that case so the rest of the matrix still
# runs.
if [[ ${istio} == ambient ]] && ! istio_ambient_installed; then
log "SKIP: tls=${tls} auth=${auth} shard=${shard} rep=${rep} istio=${istio} (ztunnel not installed)"
skipped=$(( skipped + 1 ))
continue
fi

log "SCENARIO: tls=${tls} auth=${auth} shard=${shard} rep=${rep} istio=${istio}"
if "${HERE}/run-scenario.sh" "${tls}" "${auth}" "${shard}" "${rep}" "${istio}"; then
passed=$(( passed + 1 ))
else
failed=$(( failed + 1 ))
failures+=("tls=${tls} auth=${auth} shard=${shard} rep=${rep} istio=${istio}")
fi
done

echo
log "Matrix summary: ${passed} passed, ${failed} failed, ${skipped} skipped"
if (( failed > 0 )); then
printf ' failed: %s\n' "${failures[@]}"
exit 1
fi

# Extra, non-matrix regressions (aclConfig+metrics, default-deny netpol,
# cross-release MEET isolation, ambient validator footguns, Prometheus
# scraping, etc.). Each one is independent of the tls/auth/shard/rep
# combinations — folding them into the matrix would just pay the
# install/teardown cost N times to exercise the same single assertion.
# Skipped when FILTER is set: filters are matrix-scoped, so the extras
# wouldn't match anyway and running them would be surprising.
if [[ -z ${FILTER:-} ]]; then
"${HERE}/run-extra-scenarios.sh"
fi
Loading