# Waits for the k3s API to become ready by polling /readyz. # Retries up to $k3sReadyRetries times with $k3sReadySleep seconds between attempts. function k3sReady() { local tries=${k3sReadyRetries:-10} local delay=${k3sReadySleep:-2} local i for ((i=1; i<=tries; i++)); do "$k3sCmd" kubectl get --raw=/readyz >/dev/null 2>&1 && return 0 sleep "$delay" done appError "k3s API not ready after $tries tries" return 1 } # Validates a YAML file against the Kubernetes API (dry-run). # $1 (file): path to the YAML file. function k3sYamlCheck() { local file="$1" [[ -n "$file" ]] || { appError "File not specified"; return 1; } [[ -f "$file" ]] || { appError "File not found: $file"; return 1; } k3sReady runFail "$k3sCmd" kubectl apply --dry-run=client -f "$file" } # Applies a YAML configuration to Kubernetes. # $1 (file): path to the YAML configuration file. function k3sYamlApply() { local file="$1" output error [[ -n "$file" ]] || { appError "File not specified"; return 1; } run output error "$k3sCmd" kubectl apply -f "$file" --validate=false --request-timeout=60s || { appError "Error applying YAML: ${error:-$output}" return 1 } } # Runs kubectl in $k3sNamespace after ensuring k3s is ready. function k3sRun() { k3sReady || return 1 "$k3sCmd" kubectl -n "$k3sNamespace" "$@" } # Lists pod names sorted alphabetically, optionally filtered by app label. # [$1] (app): app label value to filter by (optional). function k3sPodList() { local args=() [[ -n "${1-}" ]] && args+=(-l "app=$1") k3sRun get pods "${args[@]}" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' --sort-by='{.metadata.name}' } # Lists pod names with their phase (name|phase), optionally filtered by app label. # [$1] (app): app label value to filter by (optional). function k3sPodListStatus() { local args=() [[ -n "${1-}" ]] && args+=(-l "app=$1") local raw raw=$(k3sRun get pods "${args[@]}" \ -o jsonpath='{range .items[*]}{.metadata.name}{"|"}{.status.phase}{"|"}{range .status.conditions[*]}{.type}{"="}{.status}{" "}{end}{"\n"}{end}' \ --sort-by='{.metadata.name}') || return 1 awk -F'|' 'NF { if ($2 == "Running" && $3 ~ /(^| )DisruptionTarget=True( |$)/) status = "Terminating" else if ($2 == "Running" && $3 !~ /(^| )Ready=True( |$)/) status = "NotReady" else status = $2 print $1 "|" status }' <<< "$raw" } # Captures the current list of pod names into an array variable. # $1 (varname): name of the array variable to populate. function k3sPodsSnapshot() { local -n __out="$1" mapfile -t __out < <( k3sRun get pods --no-headers 2>/dev/null | awk '{print $1}' ) } # Waits until all pods not in the baseline snapshot are Running/Succeeded/Completed. # $1 (varname): name of the baseline array variable (from k3sPodsSnapshot). # [$2] (timeout): max wait in seconds (default 240). # [$3] (interval): poll interval in seconds (default 4). function k3sWaitNewPodsReady() { local -n baseline="$1" local timeout="${2:-240}" local interval="${3:-4}" local attempts=$(( timeout / interval )) (( attempts < 1 )) && attempts=1 local i notReady newSeen for ((i=1; i<=attempts; i++)); do mapfile -t _now < <( k3sRun get pods --no-headers 2>/dev/null | awk '{print $1, $3}' ) notReady=0 newSeen=0 local ln name status for ln in "${_now[@]}"; do [[ -z "$ln" ]] && continue name="${ln%% *}" status="${ln#* }" if ! arrayContains "$name" "${baseline[@]}"; then newSeen=1 if [[ "$status" != "Running" && "$status" != "Succeeded" && "$status" != "Completed" ]]; then ((notReady++)) fi fi done if [[ $newSeen -eq 0 || $notReady -eq 0 ]]; then return 0 fi if [[ $i -lt $attempts ]]; then printDotText "pods not ready: $fontBlue$notReady$fontReset" "${fontYellow}waiting$fontReset" sleep "$interval" else printDotText "pods not ready: $fontBlue$notReady$fontReset" "${fontRed}waiting time's up$fontReset" return 1 fi done }