mariadbLabel="[MariaDB]" # Prepares Kubernetes secrets for MariaDB. function cmdMariadbPreparation() { printInfo "$mariadbLabel Preparation..." local error runError error k3sRun delete secret "$mariadbKube-secret" --ignore-not-found || { printDotText "preparation" "$labelFail" printDanger "Delete old Secret: $error" return 1 } runError error k3sRun create secret generic "$mariadbKube-secret" --from-literal=root-password="$mariadbRootPass" --from-literal=replication-password="$mariadbRootPass" || { printDotText "preparation" "$labelFail" printDanger "Create new Secret: $error" return 1 } printDotText "preparation" "$labelDone" } # Renders the MariaDB Kubernetes YAML manifest via Helm. function cmdMariadbYamlRender() { local templateFile="templates/$mariadbKube.yaml" printSection "Render YAML file | Helm" printDotText "Template" "$appAssetsPath/k3s/$templateFile" [[ -f "$appAssetsPath/k3s/$templateFile" ]] || { printDanger "Template file not found" return 1 } local profileCpuFile="$appAssetsPath/k3s/profiles/cpu-$kubeCpuProfile.yaml" printDotText "CPU profile" "$profileCpuFile" [[ -f "$profileCpuFile" ]] || { printDanger "CPU profile file not found" return 1 } local profileMemoryFile="$appAssetsPath/k3s/profiles/memory-$kubeMemoryProfile.yaml" printDotText "Memory profile" "$profileMemoryFile" [[ -f "$profileMemoryFile" ]] || { printDanger "Memory profile file not found" return 1 } local varsFile varsFile=$(mktemp "/tmp/$mariadbKube.vars.XXXXXX.yaml") || { printDotText "render" "$labelFail" printDanger "Create temp variables file" return 1 } printDotText "Variables" "$varsFile" trap 'rm -f -- "$varsFile"' RETURN cat > "$varsFile" < "$mariadbYaml" || { printDotText "render" "$labelFail" printDanger "Render failed" return 1 } printDotText "render" "$labelDone" } # Waits until both MariaDB master and slave respond to SQL queries. function cmdMariadbWaitMasterSlave() { local error step attempts=15 for ((step=1; step<=attempts; step++)); do runError error mariadbMasterRootQuery "SELECT 1;" && break if [[ "$step" -ne "$attempts" ]]; then printWarning "$mariadbLabel Master node | Waiting..." sleep 4 else printDanger "$mariadbLabel Master node | Not responding" return 1 fi done for ((step=1; step<=attempts; step++)); do runError error mariadbSlaveRootQuery "SELECT 1;" && break if [[ "$step" -ne "$attempts" ]]; then printWarning "$mariadbLabel Slave node | Waiting..." sleep 4 else printDanger "$mariadbLabel Slave node | Not responding" return 1 fi done } # Rebuilds MariaDB replication from master to slave. # [$1] (masterPassword): replication password (defaults to $mariadbRootPass). function cmdMariadbReplicaRebuild() { local masterPassword="${1:-$mariadbRootPass}" printInfo "$mariadbLabel Replica rebuild..." local output error runError error mariadbMasterRootQuery "SELECT 1;" || { printDanger "$mariadbLabel Master node | Not responding: $error" return 1 } runError error mariadbSlaveRootQuery "SELECT 1;" || { printDanger "$mariadbLabel Slave node | Not responding: $error" return 1 } runError error mariadbMasterRootQuery "CREATE USER IF NOT EXISTS 'replication_user'@'%' IDENTIFIED BY '$masterPassword'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'replication_user'@'%'; ALTER USER 'replication_user'@'%' IDENTIFIED BY '$masterPassword';" || { printDanger "$mariadbLabel Master node | Recreated replication user: $error" return 1 } printInfo "$mariadbLabel Master node | Recreated replication user" local dumpFile="$appDataPath/$mariadbMasterKube/${appDate}_${appTime}_replica_rebuild.sql" printInfo "$mariadbLabel Master node | Exporting full dump..." mariadbMasterRootExport all "$dumpFile" || { printDanger "$mariadbLabel Master node | Export failed" return 1 } printInfo "$mariadbLabel Master node | Exported: $dumpFile" local masterFile masterPos masterFile=$(grep -m1 -oE "MASTER_LOG_FILE='[^']+'" "$dumpFile" | sed "s/MASTER_LOG_FILE='//;s/'//") masterPos=$(grep -m1 -oE "MASTER_LOG_POS=[0-9]+" "$dumpFile" | sed 's/MASTER_LOG_POS=//') [[ -n "$masterFile" && -n "$masterPos" ]] || { printDanger "$mariadbLabel Master node | Cannot parse MASTER_LOG_FILE/MASTER_LOG_POS from dump" return 1 } printInfo "$mariadbLabel Master node | Binlog: $masterFile:$masterPos" printInfo "$mariadbLabel Slave node | Stopping replication..." runError error mariadbSlaveRootQuery "STOP SLAVE; RESET SLAVE ALL;" || { printDanger "$mariadbLabel Slave node | Stop/reset failed: $error" return 1 } printInfo "$mariadbLabel Slave node | Importing full dump..." runShell output error k3sRun exec -i pod/"$mariadbSlaveKube"-0 -c "$mariadbSlaveKube" -- mariadb -u "root" -p"$mariadbRootPass" "<" "$dumpFile" ">" /dev/null || { printDanger "$mariadbLabel Slave node | Import failed: ${error:-$output}" return 1 } printInfo "$mariadbLabel Slave node | Import completed" runError error mariadbSlaveRootQuery "CHANGE MASTER TO MASTER_HOST='${mariadbMasterKube}', MASTER_PORT=3306, MASTER_USER='replication_user', MASTER_PASSWORD='${masterPassword}', MASTER_LOG_FILE='${masterFile}', MASTER_LOG_POS=${masterPos}; START SLAVE;" || { printDanger "$mariadbLabel Slave node | Configure replication failed: $error" return 1 } printInfo "$mariadbLabel Slave node | Replication configured" run output error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G" showColumn || { printDanger "$mariadbLabel Slave node | Status: $error" return 1 } local ioRunning sqlRunning secondsBehind lastError ioRunning=$(printf '%s\n' "$output" | awk -F': ' '/^ *Slave_IO_Running:/{print $2}' | tr -d '[:space:]') sqlRunning=$(printf '%s\n' "$output" | awk -F': ' '/^ *Slave_SQL_Running:/{print $2}' | tr -d '[:space:]') secondsBehind=$(printf '%s\n' "$output" | awk -F': ' '/^ *Seconds_Behind_Master:/{print $2}' | tr -d '[:space:]') lastError=$(printf '%s\n' "$output" | awk -F': ' '/^ *Last_Error:/{print substr($0, index($0,$2))}') if [[ "$ioRunning" != "Yes" || "$sqlRunning" != "Yes" ]]; then printWarning "$mariadbLabel Slave node | IO:${ioRunning:-?} | SQL:${sqlRunning:-?}" [[ -n "$lastError" ]] && printWarning "$mariadbLabel Slave node | Last error: $lastError" return 1 fi printSuccess "$mariadbLabel Replica rebuild completed | Delay ${secondsBehind:-0}s" } # Updates MariaDB Kubernetes resources (limits, config, etc.) without re-initialization. function cmdMariadbUpdate() { cmdMariadbYamlRender || return 1 cmdK3sYamlApplyEx "$mariadbYaml" || return 1 } # Installs MariaDB into Kubernetes and initializes replication. function cmdMariadbInstall() { cmdMariadbPreparation || return 1 cmdMariadbYamlRender || return 1 cmdK3sYamlApplyEx "$mariadbYaml" || return 1 cmdMariadbWaitMasterSlave || return 1 cmdMariadbReplicaRebuild || return 1 } # Uninstalls MariaDB Kubernetes resources. function cmdMariadbUninstall() { "$k3sCmd" kubectl delete -f "$mariadbYaml" } # Checks MariaDB root password, replication health, database/user count, and total data size. function cmdMariadbInfo() { local password password=$(mariadbRootPassSecretGet) if [[ "$password" != "$mariadbRootPass" ]]; then printRow printDotText "Root password" "$labelFail" printDanger "Use mariadbRootPassSecretSave" return 1 fi local podList output error pod phase # Master if ! run podList error k3sPodListStatus "$mariadbMasterKube"; then printDanger "Get pods (master): $error" elif [[ -z "$podList" ]]; then printDanger "Pods (master) not found" else while IFS='|' read -r pod phase; do printSection "$pod" if [[ "$phase" != "Running" ]]; then printDotText "Phase" "$fontRed$phase$fontReset" else printDotText "Phase" "$fontGreen$phase$fontReset" if ! run output error mariadbMasterRootQuery "SELECT VERSION();"; then printDotText "MariaDB status" "$fontRed$labelFail$fontReset" printDanger "$error" continue fi printDotText "MariaDB status" "$labelRunning" printDotText "MariaDB version" "$output" local masterStatus file position activeConnections if ! run masterStatus error mariadbMasterRootQuery "SHOW MASTER STATUS\G;" "showColumn"; then printDotText "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "$error" continue fi file=$(printf '%s\n' "$masterStatus" | awk '/^ *File:/{print $2}') if [[ -z "$file" ]]; then printDotText "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "Params 'File' not found" continue fi position=$(printf '%s\n' "$masterStatus" | awk '/^ *Position:/{print $2}') if [[ -z "$position" ]]; then printDotText "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "Params 'Position' not found" continue fi if ! run output error mariadbMasterRootQuery "SHOW STATUS LIKE 'Threads_connected';"; then printDotText "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "$error" continue fi printDotText "Replica role" "${fontGreen}master$fontReset" activeConnections=$(printf '%s\n' "$output" | awk '{print $2}') if [[ "$activeConnections" -ge 100 || "$activeConnections" -eq 1 ]]; then printDotText "Active connections" "$fontYellow$activeConnections$fontReset" else printDotText "Active connections" "$fontGreen$activeConnections$fontReset" fi fi done < <(awk 'NF' <<< "$podList") fi # Slave if ! run podList error k3sPodListStatus "$mariadbSlaveKube"; then printDanger "Get pods (slave): $error" elif [[ -z "$podList" ]]; then printDanger "Pods (slave) not found" else while IFS='|' read -r pod phase; do printSection "$pod" if [[ "$phase" != "Running" ]]; then printDotText "Phase" "$fontRed$phase$fontReset" else printDotText "Phase" "$fontGreen$phase$fontReset" if ! run output error mariadbSlaveRootQuery "SELECT VERSION();"; then printDotText "MariaDB status" "$fontRed$labelFail$fontReset" printDanger "$error" continue fi printDotText "MariaDB status" "$labelRunning" printDotText "MariaDB version" "$output" local slaveStatus slaveIoRunning slaveSqlRunning lastError secondsBehindMaster if ! run slaveStatus error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G;" "showColumn"; then printDotText "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "$error" continue fi slaveIoRunning=$(printf '%s\n' "$slaveStatus" | awk '/^ *Slave_IO_Running:/{print $2}') if [[ "$slaveIoRunning" != "Yes" ]]; then printDotText "Slave IO Running" "$fontRed$slaveIoRunning$fontReset" continue fi slaveSqlRunning=$(printf '%s\n' "$slaveStatus" | awk '/^ *Slave_SQL_Running:/{print $2}') if [[ "$slaveSqlRunning" != "Yes" ]]; then printDotText "Slave SQL Running" "$fontRed$slaveSqlRunning$fontReset" continue fi lastError=$(printf '%s\n' "$slaveStatus" | awk '/^ *Last_Error:/{$1=""; sub(/^[ \t]+/,""); print}') if [[ -n "$lastError" ]]; then printDotText "Slave SQL Running" "$fontRed$slaveSqlRunning$fontReset" printDanger "Last error: $lastError" continue fi printDotText "Replica role" "${fontGreen}slave$fontReset" secondsBehindMaster=$(printf '%s\n' "$slaveStatus" | awk '/^ *Seconds_Behind_Master:/{print $2}') if [[ "$secondsBehindMaster" -ne 0 ]]; then printDotText "Replication lags" "$fontYellow${secondsBehindMaster}s$fontReset" else printDotText "Replication lags" "${fontGreen}0s$fontReset" fi fi done < <(awk 'NF' <<< "$podList") fi printSection "Database" local databaseList userList dataSize if run output error mariadbDatabaseListGet; then mapfile -t databaseList < <(awk 'NF' <<< "$output") printDotText "Databases" "${#databaseList[@]}" else printDotText "Databases" "$labelUnknown" printDanger "$error" fi if run output error mariadbUserListGet; then mapfile -t userList < <(awk 'NF' <<< "$output") printDotText "Users" "${#userList[@]}" else printDotText "Users" "$labelUnknown" printDanger "$error" fi if ! run dataSize error mariadbMasterRootQuery "SELECT ROUND(COALESCE(SUM(DATA_LENGTH + INDEX_LENGTH), 0) / 1024 / 1024 / 1024, 2) AS t FROM information_schema.TABLES WHERE TABLE_TYPE = 'BASE TABLE';"; then printDotText "Size" "$labelUnknown" printDanger "$error" else printDotText "Size" "$dataSize Gb" fi printRow } # Shows MariaDB master and slave replication status. function cmdMariadbStatus() { local output error printSection "$mariadbMasterKube"-0 if ! run output error mariadbMasterRootQuery "SHOW MASTER STATUS;"; then printDanger "$error" else printText "$output" fi printSection "$mariadbSlaveKube"-0 if ! run output error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G;" showColumn; then printDanger "$error" else printText "$output" fi printRow } # Lists all MariaDB databases. function cmdMariadbDatabase() { local output error printRow if ! run output error mariadbDatabaseListGet; then printDanger "$error" elif [[ -z "$output" ]]; then printText "$labelNull" else printText "$output" fi printRow } # Lists all MariaDB users. function cmdMariadbUser() { local output error printRow if ! run output error mariadbUserListGet; then printDanger "$error" elif [[ -z "$output" ]]; then printText "$labelNull" else printText "$output" fi printRow } # Exports a full dump from the MariaDB master node. # [$@] (...): arguments passed to mariadbMasterRootExport (e.g. database name, file). function cmdMariadbMasterDump() { local output error printRow if ! run output error mariadbMasterRootExport "$@"; then printDanger "$error" else printText "$output" fi printRow } # Exports a full dump from the MariaDB slave node. # [$@] (...): arguments passed to mariadbSlaveRootExport (e.g. database name, file). function cmdMariadbSlaveDump() { local output error printRow if ! run output error mariadbSlaveRootExport "$@"; then printDanger "$error" else printText "$output" fi printRow }