Files
SODEW/sodew-bash-main/libs/modules/redis.sh
T
2026-08-18 09:40:08 +02:00

208 lines
7.3 KiB
Bash

# Executes a command inside the Redis master pod.
# $@ (...): command and arguments to execute.
function redisExec() {
k3sRun exec pod/"$redisKube"-0 -c "$redisKube" -- "$@"
}
# Executes a command inside a specific Redis or Redis Sentinel pod.
# Container is selected automatically based on the pod name prefix.
# $1 (pod): pod name.
# $2+ (...): command and arguments to execute.
function redisPodExec() {
local pod="$1"
[[ -n "$pod" ]] || { appError "Pod not specified"; return 1; }
shift || true
local container="$redisKube"
[[ "$pod" == "$redisSentinelKube-"* ]] && container="$redisSentinelKube"
k3sRun exec pod/"$pod" -c "$container" -- "$@"
}
# Runs redis-cli against the master pod, with authentication if configured.
# $@ (...): redis-cli arguments.
function redisExecCli() {
local -a cmd=(redis-cli --no-auth-warning)
[[ -n "$redisRootPass" ]] && cmd+=(-a "$redisRootPass")
redisExec "${cmd[@]}" "$@"
}
# Runs redis-cli on a specific pod, with authentication and port selected automatically.
# Uses port 26379 for Sentinel pods.
# $1 (pod): pod name.
# $2+ (...): redis-cli arguments.
function redisPodExecCli() {
local pod="$1"
shift || true
local -a cmd=(redis-cli --no-auth-warning)
[[ -n "$redisRootPass" ]] && cmd+=(-a "$redisRootPass")
[[ "$pod" == "$redisSentinelKube-"* ]] && cmd+=(-p 26379)
redisPodExec "$pod" "${cmd[@]}" "$@"
}
# Converts a domain name into a Redis-safe identifier (max 64 chars).
# $1 (domain): domain name.
function redisDomain2id() {
domainToRandom "$1" 64
}
# Reads the Redis root password from the Kubernetes secret.
function redisRootPassSecretGet() {
k3sRun get secret "$redisKube-secret" -o jsonpath="{.data.root-password}" --ignore-not-found | base64 -d
}
# Saves the Redis root password from the Kubernetes secret to a local file.
function redisRootPassSecretSave() {
local password
password="$(redisRootPassSecretGet)"
[[ -n "$password" ]] && printf '%s\n' "$password" > "$appDataPath/$hostName.$redisKube"
}
# Updates the Redis root password across pods. Not yet implemented.
function redisRootPassUpdate() {
printWarning "In progress..."
# Add update pass in RedisSentinel and HAProxy !!!...
# k3sRun create secret generic "$redisKube-secret" --from-literal=root-password="$redisRootPass" --dry-run=client -o yaml | k3sRun apply -f -
# k3sRun delete pod "$redisKube-0"
# sleep 1
# k3sRun delete pod "$redisKube-1"
# k3sRun rollout restart "sts/$redisSentinelKube"
# return 1
}
# List Redis users via ACL LIST (names only).
function redisUserListGet() {
local output error
run output error redisExecCli ACL LIST || { appError "$error"; return 1; }
printf '%s' "$output" | grep -oP 'user \K\w+'
}
# Flushes all keys from the current Redis database.
function redisDatabaseFlush() {
runFail redisExecCli FLUSHDB
}
# Persists the ACL user list to disk.
function redisUserListSave() {
runFail redisExecCli ACL SAVE
}
# Creates or updates a Redis ACL user with password and key pattern.
# $1 (username): ACL username.
# $2 (password): ACL password.
# [$3] (keyPattern): key access pattern (defaults to "username:*").
function redisUserSet() {
local username="$1"
[[ -n "$username" ]] || { appError "Username not specified"; return 1; }
local password="$2"
[[ -n "$password" ]] || { appError "Password not specified"; return 1; }
local keyPattern="${3:-${username}:*}"
local podList error
run podList error k3sPodList "$redisKube" || { appError "Get pods list: $error"; return 1; }
[[ -n "$podList" ]] || { appError "Pods not found"; return 1; }
while read -r pod; do
runFail redisPodExecCli "$pod" ACL SETUSER "$username" reset on sanitize-payload resetchannels ">$password" "~$keyPattern" -@all +@connection +@string +@keyspace +@sortedset +@scripting +@transaction +info -keys -flushdb -flushall '-script|flush' '-client|kill' '-client|pause' || return 1
runFail redisPodExecCli "$pod" ACL SAVE || return 1
done <<< "$podList"
}
# Removes a Redis ACL user from all pods.
# $1 (username): ACL username.
function redisUserRemove() {
local username="$1"
[[ -n "$username" ]] || { appError "Username not specified"; return 1; }
local podList error
run podList error k3sPodList "$redisKube" || { appError "Get pods list: $error"; return 1; }
[[ -n "$podList" ]] || { appError "Pods not found"; return 1; }
while read -r pod; do
runFail redisPodExecCli "$pod" ACL DELUSER "$username" || return 1
runFail redisPodExecCli "$pod" ACL SAVE || return 1
done <<< "$podList"
}
# Deletes all Redis keys matching the given prefix.
# $1 (prefixKey): key prefix to match (e.g. "user:").
function redisKeysRemove() {
local prefixKey="$1"
[[ -n "$prefixKey" ]] || { appError "PrefixKey not specified"; return 1; }
local output error
run output error redisExecCli --scan --pattern "${prefixKey}*" || { appError "$error"; return 1; }
[[ -z "$output" ]] && return 0
local -a keys
mapfile -t keys <<< "$output"
runFail redisExecCli DEL "${keys[@]}"
}
# Removes all Redis keys belonging to a site's user.
# $1 (domain): site domain name.
function redisDomainClean() {
local domain="$1"
domain=$(domainPrepare "$domain")
domainCheck "$domain" || return 1
local redisUser
redisUser=$(siteConfigGet "$domain" "redisUser")
[[ -n "$redisUser" ]] && redisKeysRemove "$redisUser:"
}
# Creates or updates the Redis ACL user and key pattern for a site.
# $1 (domain): site domain name.
function redisConfigRebuild() {
local domain
domain=$(domainPrepare "$1")
domainCheck "$domain" || return 1
fileBackup "$redisPath/$redisFileUsersAcl"
local redisUser redisPass
redisUser=$(siteConfigGetOrSet "$domain" "redisUser" "$(redisDomain2id "$domain")")
redisPass=$(siteConfigGetOrCreate "$domain" "redisPass")
redisUserSet "$redisUser" "$redisPass" || return 1
}
# Parses raw SENTINEL REPLICAS output into tab-separated lines (name, ip, port, master-host, runid).
# Skips disconnected/s_down replicas and deduplicates by runid.
# $1 (raw): raw output from `SENTINEL replicas <master>`.
function redisSentinelParseReplicas() {
local raw="$1"
local key value flags
local -A slaveData=()
local -A seenRunIds=()
while read -r key && read -r value; do
if [[ "$key" == "name" && ${#slaveData[@]} -gt 0 ]]; then
flags="${slaveData[flags]}"
if [[ -n "${slaveData[runid]}" && "$flags" != *disconnected* && "$flags" != *s_down* ]]; then
if [[ -z "${seenRunIds[${slaveData[runid]}]}" ]]; then
seenRunIds["${slaveData[runid]}"]=1
printf '%s\t%s\t%s\t%s\t%s\n' "${slaveData[name]}" "${slaveData[ip]}" "${slaveData[port]}" "${slaveData[master-host]}" "${slaveData[runid]}"
fi
fi
slaveData=()
fi
slaveData["$key"]="$value"
done <<< "$raw"
if [[ ${#slaveData[@]} -gt 0 ]]; then
flags="${slaveData[flags]}"
if [[ -n "${slaveData[runid]}" && "$flags" != *disconnected* && "$flags" != *s_down* ]]; then
if [[ -z "${seenRunIds[${slaveData[runid]}]}" ]]; then
printf '%s\t%s\t%s\t%s\t%s\n' "${slaveData[name]}" "${slaveData[ip]}" "${slaveData[port]}" "${slaveData[master-host]}" "${slaveData[runid]}"
fi
fi
fi
}