From 86daeb2a66686e075b2394160b62c74c5416bf23 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 26 Oct 2026 21:13:08 +0000 Subject: [PATCH] Add extra/etc/check-containers --- extra/etc/check-containers | 890 +++++++++++++++++++++++++++++++++++++ 1 file changed, 890 insertions(+) create mode 100644 extra/etc/check-containers diff --git a/extra/etc/check-containers b/extra/etc/check-containers new file mode 100644 index 0000000..59cf661 --- /dev/null +++ b/extra/etc/check-containers @@ -0,0 +1,890 @@ +#!/usr/bin/env bash +# check-containers +# +# statuses: +# online = http response exactly 200 +# error = reachable but non-200 response +# offline = dns/tls/connectivity/timeout failure +# skipped = unlabeled / wildcard / intentionally not checked +# +# exit codes: +# 0 = all checked domains online, or domains-only output +# 1 = at least one error +# 2 = at least one offline +# 3 = runtime/config/dependency error + +set -u +set -o pipefail + +if [ -n "${ZSH_VERSION:-}" ]; then + emulate -L sh +fi + +IFS=' + ' + +PROGRAM="check-containers" +VERSION="1.1.1" + +CONFIG_FILE="${CHECK_CONTAINERS_CONFIG:-/etc/check-containers.conf}" + +USE_COLOR="${CHECK_CONTAINERS_COLOR:-auto}" +DOMAINS_ONLY=0 +LABEL_KEY="${CHECK_CONTAINERS_LABEL_KEY:-}" +AUTO_DISCOVER="${CHECK_CONTAINERS_AUTO_DISCOVER:-1}" +NO_HEADER=0 +TIMEOUT="${CHECK_CONTAINERS_TIMEOUT:-8}" +FORCE_SCHEME="${CHECK_CONTAINERS_SCHEME:-}" +SHOW_ALL="${CHECK_CONTAINERS_SHOW_ALL:-0}" +MONITOR=0 +REFRESH="${CHECK_CONTAINERS_REFRESH:-10}" +OUTPUT_FORMAT="${CHECK_CONTAINERS_OUTPUT:-table}" +STRICT_TLS="${CHECK_CONTAINERS_STRICT_TLS:-1}" +EDIT_CONFIG=0 +INTERACTIVE=0 +EXIT_MODE="${CHECK_CONTAINERS_EXIT_MODE:-default}" +OS_FAMILY="unknown" +OS_NAME="unknown" + +RESET='' +BOLD='' +DIM='' +RED='' +GREEN='' +YELLOW='' +BLUE='' +MAGENTA='' +CYAN='' +GRAY='' + +usage() { + cat <<'USAGE' +usage: + check-containers [options] + check-containers monitor [options] + +options: + -c, --color force color output + -n, --no-color disable color output + -d, --domains print domains only + --label key docker label key to use + --no-discover disable auto-detection of domain label key + --no-header do not print table header + --timeout seconds http timeout per domain (default: 8) + --http check http only + --https check https only + --all include containers without valid domain labels + --refresh seconds monitor refresh interval (default: 10) + --json output json + --table output table + --strict-tls verify tls certificates + --insecure ignore tls certificate validation + --config file use alternate config file + --edit-config edit config interactively with prompts + --interactive prompt for missing config values before run + --exit-default 0 all online, 1 any error, 2 any offline, 3 runtime failure + -h, --help show help + +commands: + monitor live refreshing view + +config precedence: + cli > environment > config file > built-in defaults + +environment: + check_containers_config + check_containers_label_key + check_containers_auto_discover + check_containers_timeout + check_containers_scheme + check_containers_show_all + check_containers_refresh + check_containers_output + check_containers_strict_tls + check_containers_color + check_containers_exit_mode + +examples: + check-containers + check-containers --label caddy + check-containers --domains + check-containers --json + check-containers monitor --refresh 5 + check-containers --edit-config +USAGE +} + +die() { + printf 'error: %s\n' "$*" >&2 + exit 3 +} + +warn() { + printf 'warning: %s\n' "$*" >&2 +} + +# a dog wearing shoes is a top 5 out of all the phenomenons + +need() { + command -v "$1" >/dev/null 2>&1 || die "missing required command: $1" +} + +trim() { + local value + value="${1:-}" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s' "$value" +} + +lower() { + printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' +} + +load_config_file() { + [ -f "$CONFIG_FILE" ] || return 0 + # shellcheck disable=SC1090 + . "$CONFIG_FILE" + LABEL_KEY="${CHECK_CONTAINERS_LABEL_KEY:-$LABEL_KEY}" + AUTO_DISCOVER="${CHECK_CONTAINERS_AUTO_DISCOVER:-$AUTO_DISCOVER}" + TIMEOUT="${CHECK_CONTAINERS_TIMEOUT:-$TIMEOUT}" + FORCE_SCHEME="${CHECK_CONTAINERS_SCHEME:-$FORCE_SCHEME}" + SHOW_ALL="${CHECK_CONTAINERS_SHOW_ALL:-$SHOW_ALL}" + REFRESH="${CHECK_CONTAINERS_REFRESH:-$REFRESH}" + OUTPUT_FORMAT="${CHECK_CONTAINERS_OUTPUT:-$OUTPUT_FORMAT}" + STRICT_TLS="${CHECK_CONTAINERS_STRICT_TLS:-$STRICT_TLS}" + USE_COLOR="${CHECK_CONTAINERS_COLOR:-$USE_COLOR}" + EXIT_MODE="${CHECK_CONTAINERS_EXIT_MODE:-$EXIT_MODE}" +} + +detect_os() { + if [ -r /etc/os-release ]; then + OS_NAME="$(awk -F= '/^id=/{gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null | head -n1)" + [ -n "$OS_NAME" ] || OS_NAME="unknown" + case "$OS_NAME" in + alpine) OS_FAMILY="alpine" ;; + arch|endeavouros|manjaro) OS_FAMILY="arch" ;; + ubuntu) OS_FAMILY="ubuntu" ;; + debian|raspbian) OS_FAMILY="debian" ;; + *) OS_FAMILY="$OS_NAME" ;; + esac + return 0 + fi + if command -v apk >/dev/null 2>&1; then + OS_FAMILY="alpine" + OS_NAME="alpine" + elif command -v pacman >/dev/null 2>&1; then + OS_FAMILY="arch" + OS_NAME="arch" + elif command -v apt-get >/dev/null 2>&1; then + OS_FAMILY="debian" + OS_NAME="debian" + fi +} + +pick_random_theme() { + local idx + idx=$(( ( $(date +%s) + $$ ) % 5 )) + + case "$idx" in + 0) + RED=$'\033[0;31m' + GREEN=$'\033[0;32m' + YELLOW=$'\033[1;33m' + BLUE=$'\033[0;34m' + MAGENTA=$'\033[0;35m' + CYAN=$'\033[0;36m' + GRAY=$'\033[0;90m' + ;; + 1) + RED=$'\033[1;31m' + GREEN=$'\033[1;32m' + YELLOW=$'\033[0;33m' + BLUE=$'\033[1;34m' + MAGENTA=$'\033[1;35m' + CYAN=$'\033[1;36m' + GRAY=$'\033[0;37m' + ;; + 2) + RED=$'\033[38;5;196m' + GREEN=$'\033[38;5;46m' + YELLOW=$'\033[38;5;226m' + BLUE=$'\033[38;5;39m' + MAGENTA=$'\033[38;5;201m' + CYAN=$'\033[38;5;51m' + GRAY=$'\033[38;5;245m' + ;; + 3) + RED=$'\033[38;5;160m' + GREEN=$'\033[38;5;34m' + YELLOW=$'\033[38;5;178m' + BLUE=$'\033[38;5;27m' + MAGENTA=$'\033[38;5;129m' + CYAN=$'\033[38;5;44m' + GRAY=$'\033[38;5;240m' + ;; + *) + RED=$'\033[0;31m' + GREEN=$'\033[38;5;70m' + YELLOW=$'\033[38;5;214m' + BLUE=$'\033[38;5;33m' + MAGENTA=$'\033[38;5;141m' + CYAN=$'\033[38;5;45m' + GRAY=$'\033[38;5;243m' + ;; + esac +} + +setup_colors() { + local tty=0 + [ -t 1 ] && tty=1 + + case "$USE_COLOR" in + 1|yes|true|always|force) USE_COLOR="force" ;; + 0|no|false|never) USE_COLOR="never" ;; + auto|*) USE_COLOR="auto" ;; + esac + + if [ "$USE_COLOR" = "never" ]; then + RESET='' BOLD='' DIM='' + RED='' GREEN='' YELLOW='' BLUE='' MAGENTA='' CYAN='' GRAY='' + return 0 + fi + + if [ "$USE_COLOR" = "force" ] || [ "$tty" -eq 1 ]; then + RESET=$'\033[0m' + BOLD=$'\033[1m' + DIM=$'\033[2m' + pick_random_theme + else + RESET='' BOLD='' DIM='' + RED='' GREEN='' YELLOW='' BLUE='' MAGENTA='' CYAN='' GRAY='' + fi +} + +prompt_default() { + local prompt default answer + prompt="${1:-}" + default="${2:-}" + printf '%s [%s]: ' "$prompt" "$default" >&2 + IFS= read -r answer || true + answer="$(trim "$answer")" + [ -n "$answer" ] || answer="$default" + printf '%s' "$answer" +} + +write_config_file() { + local dir + dir=$(dirname "$CONFIG_FILE") + [ -d "$dir" ] || mkdir -p "$dir" || die "cannot create config directory: $dir" + + cat > "$CONFIG_FILE" <&2 + printf '%sdetected os:%s %s (%s)\n' "$CYAN" "$RESET" "$OS_NAME" "$OS_FAMILY" >&2 + + LABEL_KEY="$(prompt_default "docker label key" "${LABEL_KEY:-caddy}")" + AUTO_DISCOVER="$(prompt_default "auto-discover label key (1/0)" "${AUTO_DISCOVER:-1}")" + TIMEOUT="$(prompt_default "http timeout seconds" "${TIMEOUT:-8}")" + FORCE_SCHEME="$(prompt_default "force scheme: empty/http/https" "${FORCE_SCHEME:-}")" + SHOW_ALL="$(prompt_default "include unlabeled containers (1/0)" "${SHOW_ALL:-0}")" + REFRESH="$(prompt_default "monitor refresh seconds" "${REFRESH:-10}")" + OUTPUT_FORMAT="$(prompt_default "output format: table/json" "${OUTPUT_FORMAT:-table}")" + STRICT_TLS="$(prompt_default "strict tls verify (1/0)" "${STRICT_TLS:-1}")" + USE_COLOR="$(prompt_default "color mode: auto/force/never" "${USE_COLOR:-auto}")" + EXIT_MODE="$(prompt_default "exit mode" "${EXIT_MODE:-default}")" + + write_config_file + printf '%ssaved config:%s %s\n' "$GREEN" "$RESET" "$CONFIG_FILE" >&2 +} + +interactive_fill_missing() { + [ -n "$LABEL_KEY" ] || LABEL_KEY="$(prompt_default "docker label key" "caddy")" +} + +json_escape() { + printf '%s' "${1:-}" | sed \ + -e 's/\\/\\\\/g' \ + -e 's/"/\\"/g' \ + -e 's/ /\\t/g' \ + -e 's/ +/\\r/g' \ + -e ':a;N;$!ba;s/\n/\\n/g' +} + +strip_domain() { + local domain + domain="$(trim "${1:-}")" + domain="${domain#http://}" + domain="${domain#https://}" + domain="${domain%%/*}" + domain="${domain%%\?*}" + domain="${domain%%\#*}" + domain="${domain%%:*}" + printf '%s' "$domain" +} + +valid_domain() { + local domain + domain="$(strip_domain "${1:-}")" + [ -n "$domain" ] || return 1 + printf '%s' "$domain" | grep -Eq '^(\*\.)?[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' +} + +split_domains() { + local raw + raw="${1:-}" + raw="${raw//$'\r'/}" + raw="${raw//$'\n'/,}" + raw="${raw//;/,}" + printf '%s\n' "$raw" | + tr ',' '\n' | + sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | + sed '/^$/d' +} + +docker_state() { + case "${1:-}" in + running) printf 'running' ;; + restarting) printf 'restarting' ;; + exited|created|paused|dead) printf 'stopped' ;; + *) printf '%s' "${1:-unknown}" ;; + esac +} + +extract_domains_from_value() { + local value line + value="${1:-}" + + if printf '%s' "$value" | grep -q 'Host('; then + printf '%s' "$value" | + grep -oE '`[^`]+`' | + tr -d '`' | + while IFS= read -r line; do + [ -n "$line" ] && printf '%s\n' "$line" + done + return 0 + fi + + split_domains "$value" +} + +discover_label_key() { + local candidate found + for candidate in \ + "${LABEL_KEY:-}" \ + caddy \ + CADDY \ + VIRTUAL_HOST \ + virtual.host \ + domain \ + domains \ + traefik.domain \ + nginxproxymanager.domain + do + [ -n "$candidate" ] || continue + found="$( + docker ps -a --format "{{.Label \"$candidate\"}}" 2>/dev/null | + grep -E '([A-Za-z0-9.-]+\.[A-Za-z]{2,}|Host\()' | head -n 1 || true + )" + if [ -n "$found" ]; then + printf '%s' "$candidate" + return 0 + fi + done + return 1 +} + +check_url() { + local url curl_args code rc + url="$1" + + curl_args="--silent --show-error --location --output /dev/null --write-out %{http_code} --connect-timeout $TIMEOUT --max-time $TIMEOUT --user-agent $PROGRAM/$VERSION" + + if [ "$STRICT_TLS" = "0" ]; then + curl_args="$curl_args --insecure" + fi + + # shellcheck disable=SC2086 + code="$(curl $curl_args "$url" 2>/dev/null)" + rc=$? + + if [ "$rc" -ne 0 ] || [ -z "$code" ] || [ "$code" = "000" ]; then + return 1 + fi + + printf '%s' "$code" + return 0 +} + +http_check() { + local domain code + domain="$1" + code="" + + case "$domain" in + \*.*) + printf 'skipped|wildcard|--' + return 0 + ;; + esac + + if [ "$FORCE_SCHEME" = "http" ]; then + if code="$(check_url "http://$domain")"; then + [ "$code" = "200" ] && + printf 'online|http 200|http' || + printf 'error|http %s|http' "$code" + else + printf 'offline|no connection|http' + fi + return 0 + fi + + if [ "$FORCE_SCHEME" = "https" ]; then + if code="$(check_url "https://$domain")"; then + [ "$code" = "200" ] && + printf 'online|http 200|https' || + printf 'error|http %s|https' "$code" + else + printf 'offline|no connection|https' + fi + return 0 + fi + + if code="$(check_url "https://$domain")"; then + [ "$code" = "200" ] && + printf 'online|http 200|https' || + printf 'error|http %s|https' "$code" + return 0 + fi + + if code="$(check_url "http://$domain")"; then + [ "$code" = "200" ] && + printf 'online|http 200|http' || + printf 'error|http %s|http' "$code" + return 0 + fi + + printf 'offline|no connection|--' +} + +print_solid_line() { + local width char i + width="${1:-80}" + char="${2:-─}" + i=0 + while [ "$i" -lt "$width" ]; do + printf '%s' "$char" + i=$((i + 1)) + done + printf '\n' +} + +collect_rows() { + local rows_tmp label raw found name state uptime created_at domains domain + local container_state result check_status check_detail check_scheme domain_found + rows_tmp="$1" + found=0 + + label="$LABEL_KEY" + if [ -z "$label" ] && [ "$AUTO_DISCOVER" = "1" ]; then + label="$(discover_label_key || true)" + fi + [ -n "$label" ] || label="caddy" + + raw="$( + docker ps -a \ + --format "{{.Names}}|{{.State}}|{{.RunningFor}}|{{.CreatedAt}}|{{.Label \"$label\"}}" \ + 2>/dev/null || true + )" + + if [ -z "$raw" ]; then + warn "no docker containers found, or docker is not accessible" + printf '%s' "$label" > "${rows_tmp}.label" + return 0 + fi + + printf '%s' "$label" > "${rows_tmp}.label" + + while IFS='|' read -r name state uptime created_at domains; do + [ -n "${name:-}" ] || continue + container_state="$(docker_state "$state")" + domain_found=0 + + if [ -n "${domains:-}" ]; then + while IFS= read -r domain; do + domain="$(strip_domain "$domain")" + valid_domain "$domain" || continue + domain_found=1 + found=1 + + if [ "$DOMAINS_ONLY" = "1" ]; then + printf '%s\t%s\n' "$name" "$domain" >> "$rows_tmp" + continue + fi + + result="$(http_check "$domain")" + IFS='|' read -r check_status check_detail check_scheme <> "$rows_tmp" + done <> "$rows_tmp" + fi + done </dev/null | sort -u +} + +render_json() { + local rows_tmp label first + rows_tmp="$1" + label="$(cat "${rows_tmp}.label" 2>/dev/null || printf '%s' "${LABEL_KEY:-caddy}")" + +# printf '{\n' +# printf ' "program": "%s",\n' "$(json_escape "$PROGRAM")" +# printf ' "version": "%s",\n' "$(json_escape "$VERSION")" +# printf ' "os_family": "%s",\n' "$(json_escape "$OS_FAMILY")" +# printf ' "os_name": "%s",\n' "$(json_escape "$OS_NAME")" + printf ' "label": "%s",\n' "$(json_escape "$label")" + printf ' "containers": [\n' + + first=1 + while IFS=$'\t' read -r name state uptime domain check_status check_detail check_scheme; do + [ -n "${name:-}" ] || continue + [ "$first" -eq 1 ] || printf ',\n' + first=0 + printf ' {"container":"%s","docker_state":"%s","running_for":"%s","domain":"%s","status":"%s","result":"%s","scheme":"%s"}' \ + "$(json_escape "$name")" \ + "$(json_escape "$state")" \ + "$(json_escape "$uptime")" \ + "$(json_escape "$domain")" \ + "$(json_escape "$check_status")" \ + "$(json_escape "$check_detail")" \ + "$(json_escape "$check_scheme")" + done < "$rows_tmp" + + printf '\n ]\n' + printf '}\n' +} + +render_table() { + local rows_tmp label + local container_width state_width uptime_width domain_width status_width detail_width scheme_width total_width + local name state uptime domain check_status check_detail check_scheme + local online error offline skipped total + local domain_count matched_count container_count + local state_color status_color + + rows_tmp="$1" + label="$(cat "${rows_tmp}.label" 2>/dev/null || printf '%s' "${LABEL_KEY:-caddy}")" + + container_width=12 + state_width=12 + uptime_width=14 + domain_width=24 + status_width=8 + detail_width=14 + scheme_width=6 + + while IFS=$'\t' read -r name state uptime domain check_status check_detail check_scheme; do + [ "${#name}" -gt "$container_width" ] && container_width="${#name}" + [ "${#state}" -gt "$state_width" ] && state_width="${#state}" + [ "${#uptime}" -gt "$uptime_width" ] && uptime_width="${#uptime}" + [ "${#domain}" -gt "$domain_width" ] && domain_width="${#domain}" + [ "${#check_status}" -gt "$status_width" ] && status_width="${#check_status}" + [ "${#check_detail}" -gt "$detail_width" ] && detail_width="${#check_detail}" + [ "${#check_scheme}" -gt "$scheme_width" ] && scheme_width="${#check_scheme}" + done < "$rows_tmp" + + total_width=$((container_width + state_width + uptime_width + domain_width + status_width + detail_width + scheme_width + 18)) + + if [ "$NO_HEADER" = "0" ]; then + printf '\n' + print_solid_line "$total_width" + printf "${BOLD}${BLUE}%-${container_width}s %-${state_width}s %-${uptime_width}s %-${domain_width}s %-${status_width}s %-${detail_width}s %-${scheme_width}s${RESET}\n" \ + "container" "docker_state" "running_for" "domain" "status" "result" "scheme" + print_solid_line "$total_width" + fi + + online=0 + error=0 + offline=0 + skipped=0 + + while IFS=$'\t' read -r name state uptime domain check_status check_detail check_scheme; do + state_color="$GRAY" + status_color="$RED" + + case "$state" in + running) state_color="$GREEN" ;; + restarting) state_color="$YELLOW" ;; + stopped) state_color="$RED" ;; + esac + + case "$check_status" in + online) + status_color="$GREEN" + online=$((online + 1)) + ;; + error) + status_color="$YELLOW" + error=$((error + 1)) + ;; + offline) + status_color="$RED" + offline=$((offline + 1)) + ;; + skipped) + status_color="$GRAY" + skipped=$((skipped + 1)) + ;; + esac + + printf "%-${container_width}s %s%-${state_width}s${RESET} %-${uptime_width}s %-${domain_width}s %s%-${status_width}s${RESET} %-${detail_width}s %-${scheme_width}s\n" \ + "$name" \ + "$state_color" "$state" \ + "$uptime" \ + "$domain" \ + "$status_color" "$check_status" \ + "$check_detail" \ + "$check_scheme" + done < "$rows_tmp" + + total=$((online + error + offline + skipped)) + domain_count="$(cut -f4 "$rows_tmp" 2>/dev/null | grep -v '^-$' | sort -u | wc -l | tr -d ' ')" + matched_count="$(cut -f1 "$rows_tmp" 2>/dev/null | sort -u | wc -l | tr -d ' ')" + container_count="$(docker ps -aq 2>/dev/null | wc -l | tr -d ' ')" + + if [ "$NO_HEADER" = "0" ]; then + print_solid_line "$total_width" + fi + + printf "${GREEN}${BOLD}online${RESET}: %d " "$online" + printf "${YELLOW}${BOLD}error${RESET}: %d " "$error" + printf "${RED}${BOLD}offline${RESET}: %d " "$offline" + printf "${GRAY}${BOLD}skipped${RESET}: %d " "$skipped" + printf "${BLUE}${BOLD}total${RESET}: %d\n" "$total" + + printf "${CYAN}domains${RESET}: %s " "$domain_count" + printf "${CYAN}matched_containers${RESET}: %s " "$matched_count" + printf "${CYAN}all_containers${RESET}: %s " "$container_count" + printf "${CYAN}label${RESET}: %s " "$label" + printf "${CYAN}os${RESET}: %s\n" "$OS_FAMILY" +} + +determine_exit_code() { + local rows_tmp status rc + rows_tmp="$1" + rc=0 + + while IFS=$'\t' read -r _ _ _ _ status _ _; do + case "$status" in + offline) rc=2 ;; + error) + [ "$rc" -lt 2 ] && rc=1 + ;; + esac + done < "$rows_tmp" + + return "$rc" +} + +run_check() { + local rows_tmp="" + rows_tmp="$(mktemp "${TMPDIR:-/tmp}/check-containers.XXXXXX")" || die "failed to create temp file" + trap '[ -n "${rows_tmp:-}" ] && rm -f "${rows_tmp}" "${rows_tmp}.label"' RETURN INT TERM EXIT + + collect_rows "$rows_tmp" + + if [ "$DOMAINS_ONLY" = "1" ]; then + render_domains_only "$rows_tmp" + return 0 + fi + + case "$OUTPUT_FORMAT" in + json) render_json "$rows_tmp" ;; + *) render_table "$rows_tmp" ;; + esac + + determine_exit_code "$rows_tmp" + return $? +} + +parse_args() { + if [ "${1:-}" = "monitor" ]; then + MONITOR=1 + shift + fi + + while [ "$#" -gt 0 ]; do + case "$1" in + -c|--color) + USE_COLOR="force" + ;; + -n|--no-color) + USE_COLOR="never" + ;; + -d|--domains) + DOMAINS_ONLY=1 + ;; + --label) + shift + [ "$#" -gt 0 ] || die "--label requires a value" + LABEL_KEY="$1" + AUTO_DISCOVER=0 + ;; + --no-discover) + AUTO_DISCOVER=0 + ;; + --no-header) + NO_HEADER=1 + ;; + --timeout) + shift + [ "$#" -gt 0 ] || die "--timeout requires whole seconds" + printf '%s' "$1" | grep -Eq '^[0-9]+$' || die "--timeout requires whole seconds" + TIMEOUT="$1" + ;; + --refresh) + shift + [ "$#" -gt 0 ] || die "--refresh requires whole seconds" + printf '%s' "$1" | grep -Eq '^[0-9]+$' || die "--refresh requires whole seconds" + REFRESH="$1" + ;; + --http) + FORCE_SCHEME="http" + ;; + --https) + FORCE_SCHEME="https" + ;; + --all) + SHOW_ALL=1 + ;; + --json) + OUTPUT_FORMAT="json" + ;; + --table) + OUTPUT_FORMAT="table" + ;; + --strict-tls) + STRICT_TLS=1 + ;; + --insecure) + STRICT_TLS=0 + ;; + --config) + shift + [ "$#" -gt 0 ] || die "--config requires a file path" + CONFIG_FILE="$1" + ;; + --edit-config) + EDIT_CONFIG=1 + ;; + --interactive) + INTERACTIVE=1 + ;; + --exit-default) + EXIT_MODE="default" + ;; + -h|--help) + usage + exit 0 + ;; + *) + die "unknown option: $1" + ;; + esac + shift + done +} + +main() { + parse_args "$@" + detect_os + load_config_file + setup_colors + + need docker + need curl + need awk + need grep + need sed + need tr + need cut + need sort + need wc + need mktemp + need date + + if [ "$EDIT_CONFIG" = "1" ]; then + interactive_edit_config + exit 0 + fi + + if [ "$INTERACTIVE" = "1" ]; then + interactive_fill_missing + fi + + if [ "$MONITOR" = "1" ]; then + trap 'printf "\n"; exit 0' INT TERM + while true; do + if [ -t 1 ]; then + printf '\033[H\033[2J' + fi +# printf "${BOLD}${CYAN}%s monitor${RESET} " "$PROGRAM" +# printf "${DIM}%s | refresh: %ss | os: %s | ctrl+c to exit${RESET}\n" \ + printf "$(date '+%Y-%m-%d %H:%M:%S')" \ + "$REFRESH" \ + "$OS_FAMILY" + run_check || true + sleep "$REFRESH" + done + else + run_check + exit $? + fi +} + +main "$@" \ No newline at end of file