#!/usr/bin/env bash set -euo pipefail KUBECONFIG_PATH="${KUBECONFIG:-}" TALOSCONFIG_PATH="${TALOSCONFIG:-}" NODE_IP="" SITE_ID="appliance-single-node" OUTPUT_DIR="${PWD}" REPO_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/../../.." && pwd)" TEMP_DIR="" usage() { cat <<'EOF' Usage: collect-support-bundle.sh --kubeconfig [options] Options: --kubeconfig Kubeconfig path --talosconfig Optional legacy Talos config path for Talos-only diagnostics --node-ip Optional legacy Talos node IP for node-level collection --site-id Bundle identifier (default: appliance-single-node) --output-dir Directory for the .tar.gz bundle (default: current directory) --help Show this help EOF } require_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "Required command not found: $1" >&2 exit 1 fi } cleanup() { if [ -n "$TEMP_DIR" ] && [ -d "$TEMP_DIR" ]; then rm -rf "$TEMP_DIR" fi } capture_cmd() { local output_file="$1" shift { printf '$' for arg in "$@"; do printf ' %q' "$arg" done printf '\n' "$@" } >"$output_file" 2>&1 || true } kubectl_capture() { local output_file="$1" shift capture_cmd "$output_file" kubectl --kubeconfig "$KUBECONFIG_PATH" "$@" } talos_capture() { local output_file="$1" shift if [ -z "$TALOSCONFIG_PATH" ] || [ -z "$NODE_IP" ]; then return 0 fi capture_cmd "$output_file" talosctl --talosconfig "$TALOSCONFIG_PATH" -n "$NODE_IP" -e "$NODE_IP" "$@" } collect_namespace_logs() { local namespace="$1" local pod_list local pod_name local container_list local container_name local safe_namespace safe_namespace="$(printf '%s' "$namespace" | tr '/' '_')" pod_list="$(kubectl --kubeconfig "$KUBECONFIG_PATH" -n "$namespace" get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)" printf '%s\n' "$pod_list" | while IFS= read -r pod_name; do [ -n "$pod_name" ] || continue container_list="$(kubectl --kubeconfig "$KUBECONFIG_PATH" -n "$namespace" get pod "$pod_name" -o jsonpath='{range .spec.initContainers[*]}{.name}{"\n"}{end}{range .spec.containers[*]}{.name}{"\n"}{end}' 2>/dev/null || true)" printf '%s\n' "$container_list" | while IFS= read -r container_name; do [ -n "$container_name" ] || continue kubectl_capture "$TEMP_DIR/logs/${safe_namespace}-${pod_name}-${container_name}.log" -n "$namespace" logs "$pod_name" -c "$container_name" --tail=500 kubectl_capture "$TEMP_DIR/logs/${safe_namespace}-${pod_name}-${container_name}-previous.log" -n "$namespace" logs "$pod_name" -c "$container_name" --previous --tail=500 done done } while [ "$#" -gt 0 ]; do case "$1" in --kubeconfig) KUBECONFIG_PATH="$2" shift 2 ;; --talosconfig) TALOSCONFIG_PATH="$2" shift 2 ;; --node-ip) NODE_IP="$2" shift 2 ;; --site-id) SITE_ID="$2" shift 2 ;; --output-dir) OUTPUT_DIR="$2" shift 2 ;; --help|-h) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 1 ;; esac done require_command kubectl require_command tar require_command date if [ -z "$KUBECONFIG_PATH" ]; then echo "Kubeconfig path is required via --kubeconfig or KUBECONFIG." >&2 exit 1 fi if [ ! -f "$KUBECONFIG_PATH" ]; then echo "Kubeconfig file not found: $KUBECONFIG_PATH" >&2 exit 1 fi if [ -n "$TALOSCONFIG_PATH" ] && [ ! -f "$TALOSCONFIG_PATH" ]; then echo "Talosconfig file not found: $TALOSCONFIG_PATH" >&2 exit 1 fi TEMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/alga-support-bundle.XXXXXX")" trap cleanup EXIT mkdir -p "$TEMP_DIR"/{cluster,logs,talos,meta} cat >"$TEMP_DIR/meta/summary.txt" <