Add extra/etc/check-containers

This commit is contained in:
dev 2026-10-26 21:13:08 +00:00
parent 85d34874c5
commit 86daeb2a66

890
extra/etc/check-containers Normal file
View File

@ -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" <<EOF
# $PROGRAM configuration
CHECK_CONTAINERS_LABEL_KEY="${LABEL_KEY}"
CHECK_CONTAINERS_AUTO_DISCOVER="${AUTO_DISCOVER}"
CHECK_CONTAINERS_TIMEOUT="${TIMEOUT}"
CHECK_CONTAINERS_SCHEME="${FORCE_SCHEME}"
CHECK_CONTAINERS_SHOW_ALL="${SHOW_ALL}"
CHECK_CONTAINERS_REFRESH="${REFRESH}"
CHECK_CONTAINERS_OUTPUT="${OUTPUT_FORMAT}"
CHECK_CONTAINERS_STRICT_TLS="${STRICT_TLS}"
CHECK_CONTAINERS_COLOR="${USE_COLOR}"
CHECK_CONTAINERS_EXIT_MODE="${EXIT_MODE}"
EOF
}
interactive_edit_config() {
printf '%sinteractive configuration for %s%s\n' "$BOLD" "$PROGRAM" "$RESET" >&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 <<EOF
$result
EOF
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$name" \
"$container_state" \
"$uptime" \
"$domain" \
"$check_status" \
"$check_detail" \
"$check_scheme" >> "$rows_tmp"
done <<EOF
$(extract_domains_from_value "$domains")
EOF
fi
if [ "$SHOW_ALL" = "1" ] && [ "$domain_found" = "0" ] && [ "$DOMAINS_ONLY" = "0" ]; then
found=1
printf '%s\t%s\t%s\t-\tskipped\tno domain label\t--\n' \
"$name" \
"$container_state" \
"$uptime" >> "$rows_tmp"
fi
done <<EOF
$raw
EOF
if [ "$found" = "0" ]; then
warn "no valid domain labels found using label key: $label"
warn "try: $PROGRAM --label your_label_key"
fi
}
render_domains_only() {
local rows_tmp
rows_tmp="$1"
cut -f2 "$rows_tmp" 2>/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 "$@"