mariadbLabel="[MariaDB]" # Prepares Kubernetes secrets for MariaDB. function cmdMariadbPreparation() { printInfo "$mariadbLabel Preparation..." local error runError error k3sRun delete secret "$mariadbKube-secret" --ignore-not-found || { printDanger "$mariadbLabel Delete old Secret: $error" return 1 } printInfo "$mariadbLabel Deleted old Secret" runError error k3sRun create secret generic "$mariadbKube-secret" --from-literal=root-password="$mariadbRootPass" --from-literal=replication-password="$mariadbRootPass" || { printDanger "$mariadbLabel Create new Secret: $error" return 1 } printInfo "$mariadbLabel Created new Secret" printSuccess "$mariadbLabel Preparatory completed" } # Renders the MariaDB Kubernetes YAML manifest via Helm. function cmdMariadbYamlRender() { printInfo "$mariadbLabel Render YAML | Helm" local templateFile="templates/$mariadbKube.yaml" [[ -f "$appAssetsPath/k3s/$templateFile" ]] || { printDanger "$mariadbLabel Template YAML : File not found | $appAssetsPath/k3s/$templateFile" return 1 } printInfo "$mariadbLabel Template YAML : $appAssetsPath/k3s/$templateFile" local profileCpuFile="$appAssetsPath/k3s/profiles/cpu-$kubeCpuProfile.yaml" [[ -f "$profileCpuFile" ]] || { printDanger "$mariadbLabel CPU profile : File not found | $profileCpuFile" return 1 } printInfo "$mariadbLabel CPU profile : $profileCpuFile" local profileMemoryFile="$appAssetsPath/k3s/profiles/memory-$kubeMemoryProfile.yaml" [[ -f "$profileMemoryFile" ]] || { printDanger "$mariadbLabel Memory profile : File not found | $profileMemoryFile" return 1 } printInfo "$mariadbLabel Memory profile : $profileMemoryFile" local varsFile varsFile=$(mktemp "/tmp/$mariadbKube.vars.XXXXXX.yaml") || return 1 printInfo "$mariadbLabel Variables : $varsFile" trap 'rm -f -- "$varsFile"' RETURN cat > "$varsFile" < "$mariadbYaml" || { printDanger "$mariadbLabel Render failed" return 1 } printSuccess "$mariadbLabel Render completed" } # 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" } # Installs MariaDB into Kubernetes and initializes replication. function cmdMariadbInstall() { cmdMariadbPreparation || return 1 cmdMariadbYamlRender || return 1 cmdK3sYamlApplyEx "$mariadbYaml" || return 1 cmdMariadbWaitMasterSlave || return 1 cmdMariadbReplicaRebuild } # 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 printDot 60 "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 printDot 60 "Phase" "$fontRed$phase$fontReset" else printDot 60 "Phase" "$fontGreen$phase$fontReset" if ! run output error mariadbMasterRootQuery "SELECT VERSION();"; then printDot 60 "MariaDB status" "$fontRed$labelFail$fontReset" printDanger "$error" continue fi printDot 60 "MariaDB status" "$labelRunning" printDot 60 "MariaDB version" "$output" local masterStatus file position activeConnections if ! run masterStatus error mariadbMasterRootQuery "SHOW MASTER STATUS\G;" "showColumn"; then printDot 60 "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "$error" continue fi file=$(printf '%s\n' "$masterStatus" | awk '/^ *File:/{print $2}') if [[ -z "$file" ]]; then printDot 60 "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 printDot 60 "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "Params 'Position' not found" continue fi if ! run output error mariadbMasterRootQuery "SHOW STATUS LIKE 'Threads_connected';"; then printDot 60 "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "$error" continue fi printDot 60 "Replica role" "${fontGreen}master$fontReset" activeConnections=$(printf '%s\n' "$output" | awk '{print $2}') if [[ "$activeConnections" -ge 100 || "$activeConnections" -eq 1 ]]; then printDot 60 "Active connections" "$fontYellow$activeConnections$fontReset" else printDot 60 "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 printDot 60 "Phase" "$fontRed$phase$fontReset" else printDot 60 "Phase" "$fontGreen$phase$fontReset" if ! run output error mariadbSlaveRootQuery "SELECT VERSION();"; then printDot 60 "MariaDB status" "$fontRed$labelFail$fontReset" printDanger "$error" continue fi printDot 60 "MariaDB status" "$labelRunning" printDot 60 "MariaDB version" "$output" local slaveStatus slaveIoRunning slaveSqlRunning lastError secondsBehindMaster if ! run slaveStatus error mariadbSlaveRootQuery "SHOW SLAVE STATUS\G;" "showColumn"; then printDot 60 "Replica role" "$fontRed$labelUnknown$fontReset" printDanger "$error" continue fi slaveIoRunning=$(printf '%s\n' "$slaveStatus" | awk '/^ *Slave_IO_Running:/{print $2}') if [[ "$slaveIoRunning" != "Yes" ]]; then printDot 60 "Slave IO Running" "$fontRed$slaveIoRunning$fontReset" continue fi slaveSqlRunning=$(printf '%s\n' "$slaveStatus" | awk '/^ *Slave_SQL_Running:/{print $2}') if [[ "$slaveSqlRunning" != "Yes" ]]; then printDot 60 "Slave SQL Running" "$fontRed$slaveSqlRunning$fontReset" continue fi lastError=$(printf '%s\n' "$slaveStatus" | awk '/^ *Last_Error:/{$1=""; sub(/^[ \t]+/,""); print}') if [[ -n "$lastError" ]]; then printDot 60 "Slave SQL Running" "$fontRed$slaveSqlRunning$fontReset" printDanger "Last error: $lastError" continue fi printDot 60 "Replica role" "${fontGreen}slave$fontReset" secondsBehindMaster=$(printf '%s\n' "$slaveStatus" | awk '/^ *Seconds_Behind_Master:/{print $2}') if [[ "$secondsBehindMaster" -ne 0 ]]; then printDot 60 "Replication lags" "$fontYellow${secondsBehindMaster}s$fontReset" else printDot 60 "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") printDot 60 "Databases" "${#databaseList[@]}" else printDot 60 "Databases" "$labelUnknown" printDanger "$error" fi if run output error mariadbUserListGet; then mapfile -t userList < <(awk 'NF' <<< "$output") printDot 60 "Users" "${#userList[@]}" else printDot 60 "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 printDot 60 "Size" "$labelUnknown" printDanger "$error" else printDot 60 "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 }