Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
cce156a
feat: support Valkey Cluster with sharding
ankitpatisfdc Jan 5, 2026
b9fe806
fix: auth for Valkey cluster
ankitpatisfdc Jan 15, 2026
08b6bd0
docs: update `README.md`s & `NOTES.txt` for cluster mode
ankitpatisfdc Jan 21, 2026
464b390
fix: schema updated with missing values
ankitpatisfdc Jan 24, 2026
4719147
feat: `Job` instead of startup script
ankitpatisfdc Feb 28, 2026
cbeb58c
feat: Istio compatibility
ankitpatisfdc Feb 28, 2026
2e0afb8
test: functional tests with `kind`
ankitpatisfdc May 1, 2026
ffca94e
test: functional tests for Istio
ankitpatisfdc May 1, 2026
306cf73
fix: multiple issues identified in testing
ankitpatisfdc May 1, 2026
eaa9223
feat: multiple Valkey clusters in single K8s cluster
ankitpatisfdc May 1, 2026
caa9d73
feat: Ambient Mesh support
ankitpatisfdc May 2, 2026
4058873
fix: multiple Ambient Mesh issues identified in testing
ankitpatisfdc May 2, 2026
6e7b2f4
test: include Ambient Mesh in test matrix
ankitpatisfdc May 3, 2026
080bd38
feat: `CLUSTER FAILOVER` upon `rollout restart`
ankitpatisfdc May 3, 2026
fe7c342
docs: clarify comment
ankitpatisfdc May 4, 2026
bacff0c
fix: expose annotations & labels on `cluster-init` job
ankitpatisfdc May 22, 2026
f7aed97
fix: cleanup `cluster-init` pod after 300s
ankitpatisfdc May 22, 2026
cf3a07b
fix: allow overriding probe times & thresholds for large dataset loading
ankitpatisfdc May 22, 2026
557ed79
fix: update stale `nodes.conf`
ankitpatisfdc May 22, 2026
59244a3
test: stale `nodes.conf` correctly updated
ankitpatisfdc May 22, 2026
544bed2
fix: `livenessProbe` & `readinessProbe` under cluster mode
ankitpatisfdc May 22, 2026
d202d5c
fix: `nodes.conf` test
ankitpatisfdc May 22, 2026
d7433dc
fix: cleanup `cluster-init` job on success
ankitpatisfdc Jul 12, 2026
e4c7f7e
test: assert on workload pods
ankitpatisfdc Jul 12, 2026
569fcfa
feat: `shutdown-on-sigterm failover` for the chart
ankitpatisfdc Jul 13, 2026
c2540f2
test: coexistence of `preStopFailover` and `shutdown-on-sigterm`
ankitpatisfdc Jul 13, 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
92 changes: 92 additions & 0 deletions functional-tests/lib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 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
}

# Wait for the cluster-init hook Job to have completed.
#
# The Job carries helm.sh/hook-delete-policy including hook-succeeded, so
# Helm deletes it the instant it completes — and because post-install
# hooks run synchronously, `helm install` has already returned by the
# time we get here. That means the Job is very often ALREADY GONE, which
# is itself proof of success: a FAILED hook makes `helm install` exit
# non-zero, so the scenario would have aborted before reaching this call.
# Therefore: if the Job is absent, treat it as completed; if it's still
# present (delete not yet propagated), wait for the condition.
#
# $1: job name (defaults to ${RELEASE}-cluster-init)
wait_for_cluster_init() {
local job=${1:-${RELEASE}-cluster-init}
if ! kctl get "job/${job}" >/dev/null 2>&1; then
# Already deleted by hook-succeeded ⇒ it completed.
return 0
fi
# Still present — wait for completion, but tolerate it vanishing
# mid-wait (Helm/TTL deleting it) as success.
kctl wait --for=condition=complete "job/${job}" --timeout=300s >/dev/null 2>&1 \
|| ! kctl get "job/${job}" >/dev/null 2>&1
}

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