a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
552 lines
24 KiB
Bash
552 lines
24 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
#########################################################################################
|
|
#### contabo-cost-monitor.sh — Track and report Contabo spending via the REST API. ####
|
|
#### Instance costs, snapshot usage, and alert thresholds with Prometheus output ####
|
|
#### Requires: bash 4+, curl, jq ####
|
|
#### ####
|
|
#### Author: Phil Connor ####
|
|
#### Contact: contact@mylinux.work ####
|
|
#### License: MIT ####
|
|
#### Version 1.01 ####
|
|
#### ####
|
|
#### Usage: ####
|
|
#### ./contabo-cost-monitor.sh --summary ####
|
|
#### ####
|
|
#### See --help for all options. ####
|
|
#########################################################################################
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Colors (pre-initialized) ─────────────────────────────────────────
|
|
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" RESET=""
|
|
|
|
setup_colors() {
|
|
if [[ "${COLOR:-auto}" == "never" ]]; then
|
|
return
|
|
fi
|
|
if [[ "${COLOR:-auto}" == "always" ]] || [[ -t 1 ]]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
DIM='\033[2m'
|
|
RESET='\033[0m'
|
|
fi
|
|
}
|
|
|
|
# ── Logging ───────────────────────────────────────────────────────────
|
|
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
|
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
|
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${DIM}[DEBUG]${RESET} $*"; fi; }
|
|
die() { err "$*"; exit 1; }
|
|
|
|
section_header() {
|
|
echo ""
|
|
echo -e " ${BOLD}${CYAN}── $1 ──${RESET}"
|
|
echo ""
|
|
}
|
|
|
|
field() {
|
|
printf " ${BOLD}%-22s${RESET} %s\n" "$1" "$2"
|
|
}
|
|
|
|
field_color() {
|
|
printf " ${BOLD}%-22s${RESET} %b\n" "$1" "$2"
|
|
}
|
|
|
|
elapsed() {
|
|
local end_time
|
|
end_time=$(date +%s)
|
|
echo "$(( end_time - START_TIME ))s"
|
|
}
|
|
|
|
# ── Defaults ──────────────────────────────────────────────────────────
|
|
RUN_MODE=""
|
|
OUTPUT_FORMAT="${CCM_FORMAT:-table}"
|
|
ALERT_THRESHOLD="${CCM_ALERT:-0}"
|
|
VERBOSE="${VERBOSE:-false}"
|
|
COLOR="${COLOR:-auto}"
|
|
|
|
# ── Credentials ───────────────────────────────────────────────────────
|
|
CONTABO_CLIENT_ID="${CONTABO_CLIENT_ID:-}"
|
|
CONTABO_CLIENT_SECRET="${CONTABO_CLIENT_SECRET:-}"
|
|
CONTABO_API_USER="${CONTABO_API_USER:-}"
|
|
CONTABO_API_PASS="${CONTABO_API_PASS:-}"
|
|
|
|
# ── State ─────────────────────────────────────────────────────────────
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
readonly SCRIPT_NAME
|
|
START_TIME=""
|
|
|
|
# ── API helpers ──────────────────────────────────────────────────────
|
|
contabo_token() {
|
|
local resp
|
|
resp=$(curl -s -d "client_id=${CONTABO_CLIENT_ID}" \
|
|
-d "client_secret=${CONTABO_CLIENT_SECRET}" \
|
|
--data-urlencode "username=${CONTABO_API_USER}" \
|
|
--data-urlencode "password=${CONTABO_API_PASS}" \
|
|
-d "grant_type=password" \
|
|
"https://auth.contabo.com/auth/realms/contabo/protocol/openid-connect/token")
|
|
local token
|
|
token=$(echo "$resp" | jq -r '.access_token // empty' 2>/dev/null)
|
|
if [[ -z "$token" ]]; then
|
|
die "Failed to obtain access token — check credentials"
|
|
fi
|
|
echo "$token"
|
|
}
|
|
|
|
contabo_api() {
|
|
local method="$1" endpoint="$2"
|
|
shift 2
|
|
local attempt=0 max_attempts=3
|
|
|
|
while (( attempt < max_attempts )); do
|
|
local http_code
|
|
http_code=$(curl -s -o /tmp/ccm_resp.json -w "%{http_code}" \
|
|
-X "$method" \
|
|
-H "Authorization: Bearer $(contabo_token)" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-request-id: $(cat /proc/sys/kernel/random/uuid 2>/dev/null || date +%s%N)" \
|
|
"https://api.contabo.com/v1${endpoint}" "$@")
|
|
|
|
verbose "API ${method} ${endpoint} → HTTP ${http_code}"
|
|
|
|
if [[ "$http_code" == "429" ]]; then
|
|
((attempt++)) || true
|
|
local wait=$(( attempt * 5 ))
|
|
warn "Rate limited — retrying in ${wait}s (attempt ${attempt}/${max_attempts})"
|
|
sleep "$wait"
|
|
continue
|
|
fi
|
|
|
|
cat /tmp/ccm_resp.json
|
|
return 0
|
|
done
|
|
|
|
err "API request failed after ${max_attempts} attempts: ${method} ${endpoint}"
|
|
return 1
|
|
}
|
|
|
|
check_credentials() {
|
|
[[ -z "$CONTABO_CLIENT_ID" ]] && die "CONTABO_CLIENT_ID not set"
|
|
[[ -z "$CONTABO_CLIENT_SECRET" ]] && die "CONTABO_CLIENT_SECRET not set"
|
|
[[ -z "$CONTABO_API_USER" ]] && die "CONTABO_API_USER not set"
|
|
[[ -z "$CONTABO_API_PASS" ]] && die "CONTABO_API_PASS not set"
|
|
}
|
|
|
|
check_deps() {
|
|
command -v curl &>/dev/null || die "curl is required"
|
|
command -v jq &>/dev/null || die "jq is required"
|
|
}
|
|
|
|
# ── Pagination helper ────────────────────────────────────────────────
|
|
fetch_all_contabo() {
|
|
local endpoint="$1" key="$2"
|
|
local page=1 size=100 all_data="[]"
|
|
while true; do
|
|
local sep="?"
|
|
[[ "$endpoint" == *"?"* ]] && sep="&"
|
|
local resp
|
|
resp=$(contabo_api GET "${endpoint}${sep}page=${page}&size=${size}")
|
|
local page_data
|
|
page_data=$(echo "$resp" | jq ".${key} // []" 2>/dev/null)
|
|
local page_count
|
|
page_count=$(echo "$page_data" | jq 'length' 2>/dev/null || echo 0)
|
|
[[ "$page_count" -eq 0 ]] && break
|
|
all_data=$(echo -e "${all_data}\n${page_data}" | jq -s 'add' 2>/dev/null)
|
|
(( page_count < size )) && break
|
|
((page++)) || true
|
|
done
|
|
echo "$all_data"
|
|
}
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# SUMMARY
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
do_summary() {
|
|
local instances
|
|
instances=$(fetch_all_contabo "/compute/instances" "data")
|
|
local instance_count
|
|
instance_count=$(echo "$instances" | jq 'length' 2>/dev/null || echo 0)
|
|
local running_count
|
|
running_count=$(echo "$instances" | jq '[.[] | select(.status == "running")] | length' 2>/dev/null || echo 0)
|
|
|
|
local snapshots
|
|
snapshots=$(fetch_all_contabo "/compute/snapshots" "data")
|
|
local snapshot_count
|
|
snapshot_count=$(echo "$snapshots" | jq 'length' 2>/dev/null || echo 0)
|
|
|
|
local object_storage
|
|
object_storage=$(fetch_all_contabo "/object-storages" "data")
|
|
local storage_count
|
|
storage_count=$(echo "$object_storage" | jq 'length' 2>/dev/null || echo 0)
|
|
local storage_tb
|
|
storage_tb=$(echo "$object_storage" | jq '[.[].totalPurchasedSpaceTB // 0] | add // 0' 2>/dev/null || echo 0)
|
|
local storage_used_bytes
|
|
storage_used_bytes=$(echo "$object_storage" | jq '[.[].usedSpaceBytes // 0] | add // 0' 2>/dev/null || echo 0)
|
|
local storage_used_gb
|
|
storage_used_gb=$(awk "BEGIN {printf \"%.1f\", ${storage_used_bytes} / 1073741824}")
|
|
|
|
# Estimate costs from instance product IDs
|
|
# Contabo uses fixed monthly pricing per product tier
|
|
local instance_cost="0.00"
|
|
while IFS=$'\t' read -r pid pname status; do
|
|
[[ -z "$pid" ]] && continue
|
|
# Extract monthly cost from product info if available
|
|
local cost_per_month="0"
|
|
# Contabo productId maps to fixed monthly rates
|
|
# These are approximations — actual billing comes from the Contabo panel
|
|
case "$pid" in
|
|
V1) cost_per_month="4.99" ;;
|
|
V2) cost_per_month="5.99" ;;
|
|
V4) cost_per_month="8.99" ;;
|
|
V8) cost_per_month="13.99" ;;
|
|
V16) cost_per_month="19.99" ;;
|
|
V24) cost_per_month="24.99" ;;
|
|
V30) cost_per_month="29.99" ;;
|
|
V45) cost_per_month="39.99" ;;
|
|
V60) cost_per_month="49.99" ;;
|
|
*) cost_per_month="0" ;;
|
|
esac
|
|
instance_cost=$(awk "BEGIN {printf \"%.2f\", ${instance_cost} + ${cost_per_month}}")
|
|
done < <(echo "$instances" | jq -r \
|
|
'.[] | "\(.productId // "—")\t\(.name // .displayName // "unknown")\t\(.status // "—")"' \
|
|
2>/dev/null)
|
|
|
|
local total_cost="$instance_cost"
|
|
|
|
# Alert check
|
|
local alert_triggered="false"
|
|
if [[ "$ALERT_THRESHOLD" != "0" ]]; then
|
|
local over
|
|
over=$(awk "BEGIN {print (${total_cost} > ${ALERT_THRESHOLD}) ? 1 : 0}")
|
|
[[ "$over" == "1" ]] && alert_triggered="true"
|
|
fi
|
|
|
|
case "$OUTPUT_FORMAT" in
|
|
json)
|
|
jq -n \
|
|
--argjson instances "$instance_count" \
|
|
--argjson running "$running_count" \
|
|
--argjson snapshots "$snapshot_count" \
|
|
--argjson object_storage "$storage_count" \
|
|
--arg storage_tb "$storage_tb" \
|
|
--arg storage_used_gb "$storage_used_gb" \
|
|
--arg instance_cost "$instance_cost" \
|
|
--arg total_cost "$total_cost" \
|
|
--arg alert_threshold "$ALERT_THRESHOLD" \
|
|
--argjson alert_triggered "$alert_triggered" \
|
|
'{
|
|
instances: $instances, running: $running,
|
|
snapshots: $snapshots,
|
|
object_storage: $object_storage,
|
|
storage_purchased_tb: ($storage_tb | tonumber),
|
|
storage_used_gb: ($storage_used_gb | tonumber),
|
|
monthly_estimate: {
|
|
instances: $instance_cost, total: $total_cost
|
|
},
|
|
alert: { threshold: $alert_threshold, triggered: $alert_triggered }
|
|
}'
|
|
;;
|
|
prometheus)
|
|
cat <<EOF
|
|
# HELP contabo_cost_monthly_estimate_euros Estimated monthly cost in euros
|
|
# TYPE contabo_cost_monthly_estimate_euros gauge
|
|
contabo_cost_monthly_estimate_euros ${total_cost}
|
|
# HELP contabo_cost_instances_euros Estimated monthly instance cost
|
|
# TYPE contabo_cost_instances_euros gauge
|
|
contabo_cost_instances_euros ${instance_cost}
|
|
# HELP contabo_cost_instances_total Total instances
|
|
# TYPE contabo_cost_instances_total gauge
|
|
contabo_cost_instances_total ${instance_count}
|
|
# HELP contabo_cost_instances_running Running instances
|
|
# TYPE contabo_cost_instances_running gauge
|
|
contabo_cost_instances_running ${running_count}
|
|
# HELP contabo_cost_snapshots_total Total snapshots
|
|
# TYPE contabo_cost_snapshots_total gauge
|
|
contabo_cost_snapshots_total ${snapshot_count}
|
|
# HELP contabo_cost_object_storage_total Total object storage buckets
|
|
# TYPE contabo_cost_object_storage_total gauge
|
|
contabo_cost_object_storage_total ${storage_count}
|
|
# HELP contabo_cost_storage_used_bytes Object storage used bytes
|
|
# TYPE contabo_cost_storage_used_bytes gauge
|
|
contabo_cost_storage_used_bytes ${storage_used_bytes}
|
|
# HELP contabo_cost_alert_triggered Whether cost alert threshold exceeded
|
|
# TYPE contabo_cost_alert_triggered gauge
|
|
contabo_cost_alert_triggered $([ "$alert_triggered" == "true" ] && echo 1 || echo 0)
|
|
EOF
|
|
;;
|
|
*)
|
|
section_header "Contabo Cost Summary"
|
|
|
|
printf " ${BOLD}%-22s %8s %10s %12s${RESET}\n" \
|
|
"RESOURCE" "COUNT" "USAGE" "MONTHLY €"
|
|
printf " %s\n" "$(printf '%.0s─' {1..56})"
|
|
|
|
printf " %-22s %8s %10s %12s\n" \
|
|
"Instances (running)" "$running_count" "—" "${instance_cost}"
|
|
printf " %-22s %8s %10s %12s\n" \
|
|
"Instances (total)" "$instance_count" "—" "—"
|
|
printf " %-22s %8s %10s %12s\n" \
|
|
"Snapshots" "$snapshot_count" "—" "included"
|
|
printf " %-22s %8s %10s %12s\n" \
|
|
"Object Storage" "$storage_count" "${storage_used_gb} GB" "included"
|
|
|
|
printf " %s\n" "$(printf '%.0s─' {1..56})"
|
|
printf " ${BOLD}%-22s %8s %10s %12s${RESET}\n" \
|
|
"TOTAL" "" "" "${total_cost}"
|
|
|
|
if [[ "$alert_triggered" == "true" ]]; then
|
|
echo ""
|
|
echo -e " ${RED}⚠ ALERT: Monthly estimate €${total_cost} exceeds threshold €${ALERT_THRESHOLD}${RESET}"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# BREAKDOWN
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
do_breakdown() {
|
|
local instances
|
|
instances=$(fetch_all_contabo "/compute/instances" "data")
|
|
local instance_count
|
|
instance_count=$(echo "$instances" | jq 'length' 2>/dev/null || echo 0)
|
|
[[ "$instance_count" -eq 0 ]] && die "No instances found"
|
|
|
|
case "$OUTPUT_FORMAT" in
|
|
json)
|
|
echo "$instances" | jq '[.[] | {
|
|
id: .instanceId, name: (.name // .displayName),
|
|
status: .status, product: .productId,
|
|
region: .region, ip: .ipConfig.v4.ip
|
|
}]'
|
|
;;
|
|
*)
|
|
section_header "Instance Cost Breakdown"
|
|
|
|
printf " ${BOLD}%-10s %-20s %-8s %-10s %-10s %10s${RESET}\n" \
|
|
"ID" "NAME" "PRODUCT" "STATUS" "REGION" "MONTHLY €"
|
|
printf " %s\n" "$(printf '%.0s─' {1..72})"
|
|
|
|
while IFS=$'\t' read -r iid iname pid status region; do
|
|
[[ -z "$iid" ]] && continue
|
|
local cost_per_month="0.00"
|
|
case "$pid" in
|
|
V1) cost_per_month="4.99" ;;
|
|
V2) cost_per_month="5.99" ;;
|
|
V4) cost_per_month="8.99" ;;
|
|
V8) cost_per_month="13.99" ;;
|
|
V16) cost_per_month="19.99" ;;
|
|
V24) cost_per_month="24.99" ;;
|
|
V30) cost_per_month="29.99" ;;
|
|
V45) cost_per_month="39.99" ;;
|
|
V60) cost_per_month="49.99" ;;
|
|
*) cost_per_month="—" ;;
|
|
esac
|
|
|
|
local status_color="$GREEN"
|
|
case "$status" in
|
|
running) status_color="$GREEN" ;;
|
|
stopped) status_color="$YELLOW" ;;
|
|
*) status_color="$RED" ;;
|
|
esac
|
|
|
|
printf " %-10s %-20s %-8s " "$iid" "${iname:0:18}" "$pid"
|
|
echo -ne "${status_color}"
|
|
printf "%-10s" "$status"
|
|
echo -ne "${RESET}"
|
|
printf " %-10s %10s\n" "${region:0:8}" "$cost_per_month"
|
|
done < <(echo "$instances" | jq -r \
|
|
'.[] | "\(.instanceId)\t\(.name // .displayName // "unknown")\t\(.productId // "—")\t\(.status // "—")\t\(.region // "—")"' \
|
|
2>/dev/null)
|
|
|
|
echo ""
|
|
field "Instances:" "$instance_count"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# RESOURCES
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
do_resources() {
|
|
# Snapshots
|
|
local snapshots
|
|
snapshots=$(fetch_all_contabo "/compute/snapshots" "data")
|
|
local snap_count
|
|
snap_count=$(echo "$snapshots" | jq 'length' 2>/dev/null || echo 0)
|
|
|
|
case "$OUTPUT_FORMAT" in
|
|
json)
|
|
local storage
|
|
storage=$(fetch_all_contabo "/object-storages" "data")
|
|
jq -n \
|
|
--argjson snapshots "$snapshots" \
|
|
--argjson object_storage "$storage" \
|
|
'{snapshots: $snapshots, object_storage: $object_storage}'
|
|
;;
|
|
*)
|
|
if [[ "$snap_count" -gt 0 ]]; then
|
|
section_header "Snapshots"
|
|
printf " ${BOLD}%-38s %-18s %-20s${RESET}\n" \
|
|
"SNAPSHOT_ID" "NAME" "CREATED"
|
|
printf " %s\n" "$(printf '%.0s─' {1..78})"
|
|
|
|
echo "$snapshots" | jq -r \
|
|
'.[] | "\(.snapshotId // .id // "—")\t\(.name // "—")\t\(.createdDate // "—")"' \
|
|
2>/dev/null \
|
|
| while IFS=$'\t' read -r sid sname screated; do
|
|
printf " %-38s %-18s %-20s\n" \
|
|
"${sid:0:36}" "${sname:0:16}" "${screated:0:19}"
|
|
done
|
|
echo ""
|
|
field "Snapshots:" "$snap_count"
|
|
fi
|
|
|
|
# Object Storage
|
|
local storage
|
|
storage=$(fetch_all_contabo "/object-storages" "data")
|
|
local storage_count
|
|
storage_count=$(echo "$storage" | jq 'length' 2>/dev/null || echo 0)
|
|
|
|
if [[ "$storage_count" -gt 0 ]]; then
|
|
section_header "Object Storage"
|
|
printf " ${BOLD}%-38s %-10s %-12s %-12s${RESET}\n" \
|
|
"STORAGE_ID" "REGION" "SIZE (TB)" "USED (GB)"
|
|
printf " %s\n" "$(printf '%.0s─' {1..74})"
|
|
|
|
echo "$storage" | jq -r \
|
|
'.[] | "\(.objectStorageId // .id // "—")\t\(.region // "—")\t\(.totalPurchasedSpaceTB // 0)\t\(.usedSpaceBytes // 0)"' \
|
|
2>/dev/null \
|
|
| while IFS=$'\t' read -r oid oregion osize oused; do
|
|
local used_gb
|
|
used_gb=$(awk "BEGIN {printf \"%.1f\", ${oused} / 1073741824}")
|
|
printf " %-38s %-10s %-12s %-12s\n" \
|
|
"${oid:0:36}" "${oregion:0:8}" "$osize" "$used_gb"
|
|
done
|
|
echo ""
|
|
field "Object Storage:" "$storage_count"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# HELP
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
show_help() {
|
|
cat <<EOF
|
|
${BOLD}${SCRIPT_NAME}${RESET} — Contabo Cost Monitor
|
|
|
|
Track and report Contabo spending via the REST API.
|
|
|
|
${BOLD}MODES${RESET}
|
|
--summary Cost summary across all resources (default)
|
|
--breakdown Per-instance cost breakdown
|
|
--resources List all billable resources (snapshots, object storage)
|
|
|
|
${BOLD}OPTIONS${RESET}
|
|
--format FMT Output: table, json, prometheus (default: table)
|
|
--alert EUROS Alert if monthly estimate exceeds threshold
|
|
--verbose Debug output
|
|
--no-color Disable colored output
|
|
--help Show this help message
|
|
|
|
${BOLD}ENVIRONMENT VARIABLES${RESET}
|
|
CONTABO_CLIENT_ID OAuth2 Client ID (required)
|
|
CONTABO_CLIENT_SECRET OAuth2 Client Secret (required)
|
|
CONTABO_API_USER API username / email (required)
|
|
CONTABO_API_PASS API password (required)
|
|
CCM_FORMAT Default output format
|
|
CCM_ALERT Default alert threshold in euros
|
|
VERBOSE Enable verbose output (true/false)
|
|
COLOR Color mode: auto, always, never
|
|
|
|
${BOLD}EXAMPLES${RESET}
|
|
# Cost summary
|
|
${SCRIPT_NAME} --summary
|
|
|
|
# Per-instance breakdown
|
|
${SCRIPT_NAME} --breakdown
|
|
|
|
# List all resources
|
|
${SCRIPT_NAME} --resources
|
|
|
|
# JSON output
|
|
${SCRIPT_NAME} --summary --format json
|
|
|
|
# Alert if monthly cost exceeds €100
|
|
${SCRIPT_NAME} --summary --alert 100
|
|
|
|
# Prometheus metrics
|
|
${SCRIPT_NAME} --summary --format prometheus
|
|
|
|
# Cron — hourly cost metrics
|
|
0 * * * * /usr/local/bin/contabo-cost-monitor.sh --summary --format prometheus --no-color > /var/lib/node_exporter/textfile/contabo_cost.prom 2>/dev/null
|
|
|
|
${BOLD}NOTES${RESET}
|
|
Contabo uses fixed monthly pricing per product tier.
|
|
Cost estimates are based on productId mapping — verify against your invoice.
|
|
Snapshots and object storage are typically included in Contabo plans.
|
|
|
|
${BOLD}EXIT CODES${RESET}
|
|
0 Success
|
|
1 Runtime error
|
|
EOF
|
|
}
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# PARSE ARGS
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--summary) RUN_MODE="summary"; shift ;;
|
|
--breakdown) RUN_MODE="breakdown"; shift ;;
|
|
--resources) RUN_MODE="resources"; shift ;;
|
|
--format) OUTPUT_FORMAT="${2:?--format requires a value}"; shift 2 ;;
|
|
--alert) ALERT_THRESHOLD="${2:?--alert requires a threshold}"; shift 2 ;;
|
|
--verbose) VERBOSE="true"; shift ;;
|
|
--no-color) COLOR="never"; shift ;;
|
|
--help|-h) setup_colors; show_help; exit 0 ;;
|
|
*) die "Unknown option: $1 (see --help)" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# MAIN
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
main() {
|
|
parse_args "$@"
|
|
setup_colors
|
|
|
|
if [[ -z "$RUN_MODE" ]]; then
|
|
RUN_MODE="summary"
|
|
fi
|
|
|
|
check_deps
|
|
check_credentials
|
|
|
|
START_TIME=$(date +%s)
|
|
|
|
case "$RUN_MODE" in
|
|
summary) do_summary ;;
|
|
breakdown) do_breakdown ;;
|
|
resources) do_resources ;;
|
|
*) die "Unknown mode: ${RUN_MODE}" ;;
|
|
esac
|
|
|
|
if [[ "$OUTPUT_FORMAT" != "prometheus" ]]; then
|
|
echo ""
|
|
field "Duration:" "$(elapsed)"
|
|
fi
|
|
}
|
|
|
|
main "$@"
|