208 lines
7.3 KiB
Bash
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
|
|
}
|