k3sLabel="[K3S]" # Interactively lists pods and stores the selected pod name in the given variable. # $1 (varname): name of the variable to store the selected pod name. function cmdK3sPodSelect() { local -n __pod="$1" printRow local podList error total run podList error k3sPodListStatus || { printDanger "k3sPodListStatus: $error"; return 1; } [[ -n "$podList" ]] || { printRow printDanger "Pods not found"; return 1; } total=$(grep -c . <<< "$podList") local i=0 line name status color num while IFS= read -r line; do [[ -z "$line" ]] && continue ((i++)) num=$(printf "%${#total}d" "$i") name="${line%%|*}" status="${line#*|}" color="$fontYellow" [[ "$status" == "Running" ]] && color="$fontGreen" [[ "$status" == "NotReady" ]] && color="$fontRed" [[ "$status" == "Terminating" ]] && color="$fontRed" printDotText "$num: $fontBlue$name$fontReset" "$color$status$fontReset" done <<< "$podList" printRow local input item selected read -r -p "Specify the pod number: " input printRow [[ -z "$input" ]] && input=0 [[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid pod number"; return 1; } item=$((10#$input)) selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$podList") [[ -n "$selected" ]] || { printDanger "Unknown pod number"; return 1; } __pod="${selected%%|*}" } # Interactively opens a shell or CLI inside a selected pod. # [$1] (cli): pass "cli" to open the native CLI (mariadb/redis-cli) instead of a shell. function cmdShell() { printTitle " Shell $@" local cli="$1" local pod cmdK3sPodSelect pod || return 1 local resource resourceEx resource=$(printf '%s' "$pod" | sed -E 's/-([0-9a-f]{8,}-[a-z0-9]+|[a-z0-9]{5}|[0-9]+)$//') resourceEx=$(printf '%s' "$pod" | sed -E 's/-([-a-z0-9]+)$//') local container=(-c "$resource") local cmd=(bash) local cmdLog=(bash) case "$resourceEx" in "$redisKube"|"$metricKube"|debug) cmd=(sh) cmdLog=(sh) ;; esac if [[ "$cli" == "cli" ]]; then case "$resource" in "$mariadbMasterKube"|"$mariadbSlaveKube") cmd=(mariadb -uroot -p"$mariadbRootPass") cmdLog=(mariadb -uroot -p"********") ;; "$redisKube") if [[ -z "$redisRootPass" ]]; then cmd=(redis-cli) cmdLog=(redis-cli) else cmd=(redis-cli --no-auth-warning -a "$redisRootPass") cmdLog=(redis-cli --no-auth-warning -a"********") fi ;; "$redisSentinelKube") if [[ -z "$redisRootPass" ]]; then cmd=(redis-cli -p 26379) cmdLog=(redis-cli -p 26379) else cmd=(redis-cli --no-auth-warning -p 26379 -a "$redisRootPass") cmdLog=(redis-cli --no-auth-warning -p 26379 -a"********") fi ;; esac fi printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace exec -it pod/$pod ${container[*]} -- ${cmdLog[*]}$fontReset" printRow k3sRun exec -it "pod/$pod" "${container[@]}" -- "${cmd[@]}" } # Interactively selects a pod and streams its logs. # [$@] (...): extra arguments passed to kubectl logs (e.g. -f, --tail=100). function cmdLog() { printTitle " Log $@" local pod cmdK3sPodSelect pod || return 1 local resource resource=$(printf '%s' "$pod" | sed -E 's/-([0-9a-f]{8,}-[a-z0-9]+|[a-z0-9]{5}|[0-9]+)$//') local container=(-c "$resource") printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace logs pod/$pod ${container[*]} $*$fontReset" printRow k3sRun logs "pod/$pod" "${container[@]}" "$@" } # Interactively selects a pod and describes it; also supports describing a node. # [$1] (target): pod (default) or node. function cmdDescribe() { local target="${1:-pod}" shift || true printTitle " Describe $@" case "$target" in pod) local pod cmdK3sPodSelect pod || return 1 printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace describe pod/$pod $*$fontReset" printRow k3sRun describe "pod/$pod" "$@" k3sRun describe "pod/$pod" "$@" ;; node) k3sRun describe node "$@" ;; *) printRow printWarning "Unsupported or empty command: $target" cmdHelpDescribe ;; esac } # Interactively selects and deletes a pod. function cmdDelete() { printTitle " Delete $@ $fontRed[DANGER]$fontReset" local pod cmdK3sPodSelect pod || return 1 printText "$fontBlue$k3sCmd kubectl -n $k3sNamespace delete pod/$pod $*$fontReset" printRow k3sRun delete "pod/$pod" "$@" } # Lists k3s resources; interactively prompts for type if not provided. # [$1] (resource): resource type abbreviation (pod, deploy, svc, etc.). function cmdK3sResourceList() { local resource="$1" if [[ ! "$resource" =~ ^(all|pod|event|pv|pvc|deploy|ds|rs|sts|svc|ing|ip|secret|cm|job|cj|ep|no|ns|cs|netpol)$ ]]; then local resList=" all|Get all resources pod|Pod event|Event pv|PersistentVolume pvc|PersistentVolumeClaim deploy|Deployment ds|DaemonSet rs|ReplicaSet sts|StatefulSet svc|Service ing|Ingress ip|IPAddress secret|Secret cm|ConfigMap job|Job cj|CronJob ep|Endpoint no|Node ns|Namespace cs|ComponentStatuses netpol|NetworkPolicies" printRow local i=0 line code info num total=$(grep -c . <<< "$resList") while IFS= read -r line; do [[ -n "$line" ]] || continue ((i++)) num=$(printf "%${#total}d" "$i") code="${line%%|*}" info="${line#*|}" printDotFix 20 "$num: $fontBlue$code$fontReset" "$info" done <<< "$resList" printRow local input item selected read -r -p "Specify the type number [0]: " input printRow [[ -z "$input" ]] && input=0 [[ "$input" =~ ^[0-9]+$ ]] || { printDanger "Invalid type number"; return 1; } item=$((10#$input)) selected=$(awk 'NF {n++} n == item {print; exit}' item="$item" <<< "$resList") [[ -n "$selected" ]] || { printDanger "Unknown type number"; return 1; } resource="${selected%%|*}" fi run output error k3sRun get "$resource" || { printDanger "$error"; return 1; } printf '%s\n' "$output" } # Installs k3s on the server. function cmdK3sInstall() { printInfo "$k3sLabel Install..." curl -sfL https://get.k3s.io | sh - || return 1 systemctl enable --now k3s } # Uninstalls k3s from the server. function cmdK3sUninstall() { /usr/local/bin/k3s-uninstall.sh } # Adds a namespace to Kubernetes if it does not already exist. # $1 (namespace): namespace name. function cmdK3sNamespaceAdd() { local output error run output error "$k3sCmd" kubectl get ns -o jsonpath="{range .items[*]}{.metadata.name}{'\n'}{end}" || { printDanger "$k3sLabel $error"; return 1; } if ! grep -qF -- "$k3sNamespace" <<< "$output"; then run output error "$k3sCmd" kubectl create ns "$k3sNamespace" || { printDanger "$k3sLabel $error"; return 1; } fi } # Deletes all resources in the current namespace. function cmdK3sResourceClean() { printWarning "$k3sLabel Namespace: $k3sNamespace | Wiping..." k3sRun delete all --all --ignore-not-found --wait=false --timeout=30s --request-timeout=60s || true k3sRun delete cm,secret,ingress,networkpolicy,svc,pvc,job,cronjob --all --ignore-not-found --wait=false --timeout=30s --request-timeout=60s || true mapfile -t pvs < <( "$k3sCmd" kubectl get pv -o jsonpath='{range .items[?(@.spec.claimRef.namespace=="'"$k3sNamespace"'")]}{.metadata.name}{"\n"}{end}' 2>/dev/null ) local pv for pv in "${pvs[@]}"; do [[ -n "$pv" ]] || continue "$k3sCmd" kubectl patch pv "$pv" --type=merge -p '{"metadata":{"finalizers":[]}}' || true "$k3sCmd" kubectl delete pv "$pv" --wait=false --timeout=15s || true done printSuccess "$k3sLabel Namespace: $k3sNamespace | Wiped" } # Validates, applies a YAML file, then waits for new pods to become ready. # $1 (file): path to the YAML configuration file. function cmdK3sYamlApplyEx() { local file="$1" output error printSection "Applying YAML file" printDotText "file" "$file" run output error k3sYamlCheck "$file" || { printDotText "applying" "$labelFail" printDanger "Error checking YAML: ${error:-$output}" return 1 } printDotText "checking" "$labelDone" k3sPodsSnapshot podList || { printDotText "applying" "$labelFail" printDanger "Failed get list of pods" return 1 } runError error k3sYamlApply "$file" || { printDotText "applying" "$labelFail" printDanger "Error applied YAML: ${error:-$output}" return 1 } printDotText "applying" "$labelDone" k3sWaitNewPodsReady podList || return 1 } # Displays pods, services, ingress, and kube-system services info. function cmdK3sInfo() { local output error line printSection "k3s" if ! run output error k3sRun version; then printDanger "$error"; else while IFS= read -r line; do printDotText "${line%% Version:*}" "${line##*: }" done < <(awk 'NF' <<< "$output") fi if ! run output error "$k3sCmd" kubectl get nodes --no-headers; then printDanger "$error" else local name status roles age version while IFS='|' read -r name status roles age version; do printSection "$name" if [[ "$status" == "Ready" ]]; then printDotText "Status" "$fontGreen$status$fontReset" else printDotText "Status" "$fontRed$status$fontReset" fi printDotText "Roles" "$roles" printDotText "Age" "$age" printDotText "Version" "$version" done < <(awk 'NF{print $1 "|" $2 "|" $3 "|" $4 "|" $5}' <<< "$output") fi } # Displays pods, services, ingress, kube-system services, and non-running pod bugs. function cmdK3sNodesStatus() { printSection "Pods" if run output error k3sRun get pods -o wide; then printText "$output" else printDanger "$error" fi printSection "Services" if run output error k3sRun get svc -o wide; then printText "$output" else printDanger "$error" fi printSection "Ingress" if run output error k3sRun get ing; then printText "$output" else printDanger "$error" fi printSection "Services (kube-system)" if run output error "$k3sCmd" kubectl -n kube-system get svc; then printText "$output" else printDanger "$error" fi printSection "Bugs..." local output error if run output error kubectl get pods -A \ --field-selector=status.phase!=Running,status.phase!=Pending \ -o custom-columns='NS:.metadata.namespace,POD:.metadata.name,PHASE:.status.phase,REASON:.status.reason,NODE:.spec.nodeName'; then printText "$output" else printDanger "$error" fi printText " PHASE | REASON ------------------ Failed | !=0 Process crashed Failed | Error Process ended with an error Failed | OOMKilled Ran out of memory Failed | Evicted Kubelet evicted the pod (disk/memory pressure) Completed/Succeeded - Not error if Job/migration/init task" } # Displays resource usage (CPU/memory) for all pods in the namespace. function cmdK3sTopPod() { local output error run output error k3sRun top pod || { printDanger "$error"; return 1; } printRow printText "$output" }