Sync all scripts from website downloads — 352 scripts total
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.
This commit is contained in:
Executable
+643
@@ -0,0 +1,643 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#########################################################################################
|
||||
#### mailcow-smoke-tests.sh — Verify Mailcow instance health after upgrades ####
|
||||
#### Zero external dependencies. Requires: bash 4+, curl, openssl, nc (netcat) ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### License: MIT ####
|
||||
#### Version 1.0 ####
|
||||
#### ####
|
||||
#### Usage: ####
|
||||
#### export MAILCOW_URL="https://mail.example.com" ####
|
||||
#### export MAILCOW_API_KEY="your-api-key" ####
|
||||
#### ./mailcow-smoke-tests.sh ####
|
||||
#### ####
|
||||
#### See --help for all options. ####
|
||||
#########################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────
|
||||
MAILCOW_URL="${MAILCOW_URL:-}"
|
||||
MAILCOW_API_KEY="${MAILCOW_API_KEY:-}"
|
||||
MAILCOW_DIR="${MAILCOW_DIR:-/opt/mailcow-dockerized}"
|
||||
CURL_TIMEOUT="${CURL_TIMEOUT:-10}"
|
||||
CURL_INSECURE="${CURL_INSECURE:-false}"
|
||||
SKIP_SMTP="${SKIP_SMTP:-false}"
|
||||
SKIP_IMAP="${SKIP_IMAP:-false}"
|
||||
SKIP_CLAMD="${SKIP_CLAMD:-false}"
|
||||
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}" # text, tap, junit
|
||||
JUNIT_FILE="${JUNIT_FILE:-smoke-results.xml}"
|
||||
VERBOSE="${VERBOSE:-false}"
|
||||
COLOR="${COLOR:-auto}"
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────
|
||||
PASS=0
|
||||
FAIL=0
|
||||
SKIP=0
|
||||
TOTAL=0
|
||||
RESULTS=()
|
||||
START_TIME=""
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────
|
||||
setup_colors() {
|
||||
if [[ "$COLOR" == "never" ]]; then
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
return
|
||||
fi
|
||||
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
RESET='\033[0m'
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
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 "${BLUE}[DEBUG]${RESET} $*"; fi; }
|
||||
|
||||
# ── Test Result Recording ─────────────────────────────────────────────
|
||||
record_pass() {
|
||||
local name="$1"
|
||||
local detail="${2:-}"
|
||||
((PASS++)) || true
|
||||
((TOTAL++)) || true
|
||||
RESULTS+=("PASS|${name}|${detail}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
echo "ok ${TOTAL} - ${name}"
|
||||
else
|
||||
echo -e " ${GREEN}✓${RESET} ${name}${detail:+ — ${detail}}"
|
||||
fi
|
||||
}
|
||||
|
||||
record_fail() {
|
||||
local name="$1"
|
||||
local detail="${2:-}"
|
||||
((FAIL++)) || true
|
||||
((TOTAL++)) || true
|
||||
RESULTS+=("FAIL|${name}|${detail}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
echo "not ok ${TOTAL} - ${name}"
|
||||
[[ -n "$detail" ]] && echo " # ${detail}"
|
||||
else
|
||||
echo -e " ${RED}✗${RESET} ${name}${detail:+ — ${detail}}"
|
||||
fi
|
||||
}
|
||||
|
||||
record_skip() {
|
||||
local name="$1"
|
||||
local reason="${2:-}"
|
||||
((SKIP++)) || true
|
||||
((TOTAL++)) || true
|
||||
RESULTS+=("SKIP|${name}|${reason}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
echo "ok ${TOTAL} - ${name} # SKIP ${reason}"
|
||||
else
|
||||
echo -e " ${YELLOW}⊘${RESET} ${name}${reason:+ — ${reason}}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── curl wrapper ──────────────────────────────────────────────────────
|
||||
api_curl() {
|
||||
local endpoint="$1"
|
||||
shift
|
||||
local curl_opts=(-s -S --max-time "$CURL_TIMEOUT")
|
||||
|
||||
[[ "$CURL_INSECURE" == "true" ]] && curl_opts+=(-k)
|
||||
curl_opts+=(-H "X-API-Key: ${MAILCOW_API_KEY}")
|
||||
curl_opts+=(-H "Content-Type: application/json")
|
||||
|
||||
local url="${MAILCOW_URL}${endpoint}"
|
||||
verbose "curl GET ${url}"
|
||||
|
||||
curl "${curl_opts[@]}" "$@" "$url" 2>/dev/null
|
||||
}
|
||||
|
||||
api_curl_status() {
|
||||
local endpoint="$1"
|
||||
shift
|
||||
local curl_opts=(-s -S -o /dev/null -w "%{http_code}" --max-time "$CURL_TIMEOUT")
|
||||
|
||||
[[ "$CURL_INSECURE" == "true" ]] && curl_opts+=(-k)
|
||||
curl_opts+=(-H "X-API-Key: ${MAILCOW_API_KEY}")
|
||||
curl_opts+=(-H "Content-Type: application/json")
|
||||
|
||||
local url="${MAILCOW_URL}${endpoint}"
|
||||
curl "${curl_opts[@]}" "$@" "$url" 2>/dev/null
|
||||
}
|
||||
|
||||
# ── JSON parsing (no jq required) ────────────────────────────────────
|
||||
json_value() {
|
||||
local key="$1"
|
||||
local json="$2"
|
||||
echo "$json" | { grep -oP "\"${key}\"\s*:\s*\"?\K[^\",}]+" || true; } | head -1
|
||||
}
|
||||
|
||||
json_value_string() {
|
||||
local key="$1"
|
||||
local json="$2"
|
||||
echo "$json" | { grep -oP "\"${key}\"\s*:\s*\"\K[^\"]*" || true; } | head -1
|
||||
}
|
||||
|
||||
# ── Host extraction ───────────────────────────────────────────────────
|
||||
get_mailcow_host() {
|
||||
echo "$MAILCOW_URL" | sed 's|https\?://||' | cut -d/ -f1 | cut -d: -f1
|
||||
}
|
||||
|
||||
# ── Port check ────────────────────────────────────────────────────────
|
||||
check_port() {
|
||||
local host="$1"
|
||||
local port="$2"
|
||||
nc -z -w 3 "$host" "$port" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# TEST SUITES
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── 1. Connectivity ──────────────────────────────────────────────────
|
||||
test_connectivity() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Connectivity${RESET}"
|
||||
|
||||
# 1a. Mailcow UI reachable
|
||||
local curl_opts=(-s -o /dev/null -w "%{http_code}" --max-time "$CURL_TIMEOUT")
|
||||
[[ "$CURL_INSECURE" == "true" ]] && curl_opts+=(-k)
|
||||
|
||||
local http_code
|
||||
http_code=$(curl "${curl_opts[@]}" "${MAILCOW_URL}/" 2>/dev/null) || http_code="000"
|
||||
|
||||
if [[ "$http_code" == "200" || "$http_code" == "301" || "$http_code" == "302" ]]; then
|
||||
record_pass "Mailcow UI reachable" "HTTP ${http_code}"
|
||||
else
|
||||
record_fail "Mailcow UI reachable" "HTTP ${http_code}"
|
||||
fi
|
||||
|
||||
# 1b. TLS certificate validity
|
||||
if [[ "$MAILCOW_URL" == https://* ]]; then
|
||||
local host
|
||||
host=$(get_mailcow_host)
|
||||
local port
|
||||
port=$(echo "$MAILCOW_URL" | grep -oP ':\K[0-9]+$' || echo "443")
|
||||
|
||||
local expiry
|
||||
expiry=$(echo | openssl s_client -servername "$host" -connect "${host}:${port}" 2>/dev/null | \
|
||||
openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) || expiry=""
|
||||
|
||||
if [[ -n "$expiry" ]]; then
|
||||
local expiry_epoch
|
||||
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null) || expiry_epoch=0
|
||||
local now_epoch
|
||||
now_epoch=$(date +%s)
|
||||
local days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
|
||||
|
||||
if [[ $days_left -gt 30 ]]; then
|
||||
record_pass "TLS certificate valid" "${days_left} days remaining"
|
||||
elif [[ $days_left -gt 0 ]]; then
|
||||
record_pass "TLS certificate valid" "${days_left} days remaining (renew soon)"
|
||||
else
|
||||
record_fail "TLS certificate valid" "expired or expiring in ${days_left} days"
|
||||
fi
|
||||
else
|
||||
record_skip "TLS certificate check" "could not retrieve certificate"
|
||||
fi
|
||||
else
|
||||
record_skip "TLS certificate check" "not using HTTPS"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 2. API ────────────────────────────────────────────────────────────
|
||||
test_api() {
|
||||
echo ""
|
||||
echo -e "${BOLD}API${RESET}"
|
||||
|
||||
# 2a. API authentication — version endpoint
|
||||
local version_json
|
||||
version_json=$(api_curl "/api/v1/get/status/version" 2>/dev/null) || version_json=""
|
||||
|
||||
local mc_version
|
||||
mc_version=$(json_value "version" "$version_json")
|
||||
|
||||
if [[ -n "$mc_version" && "$mc_version" != "null" ]]; then
|
||||
record_pass "API authentication" "Mailcow ${mc_version}"
|
||||
elif [[ "$version_json" == *"error"* || "$version_json" == *"unauthorized"* ]]; then
|
||||
record_fail "API authentication" "API key rejected"
|
||||
else
|
||||
record_fail "API authentication" "no version returned"
|
||||
fi
|
||||
|
||||
# 2b. Container status
|
||||
local containers_json
|
||||
containers_json=$(api_curl "/api/v1/get/status/containers" 2>/dev/null) || containers_json=""
|
||||
|
||||
if [[ -n "$containers_json" && "$containers_json" != *"error"* ]]; then
|
||||
local running_count=0
|
||||
local stopped_count=0
|
||||
local container_names=""
|
||||
|
||||
# Count running vs non-running containers
|
||||
running_count=$(echo "$containers_json" | { grep -oP '"state"\s*:\s*"running"' || true; } | wc -l)
|
||||
stopped_count=$(echo "$containers_json" | { grep -oP '"state"\s*:\s*"(?!running)[^"]*"' || true; } | wc -l)
|
||||
|
||||
if [[ $running_count -gt 0 && $stopped_count -eq 0 ]]; then
|
||||
record_pass "Container status" "all ${running_count} containers running"
|
||||
elif [[ $running_count -gt 0 ]]; then
|
||||
record_fail "Container status" "${running_count} running, ${stopped_count} not running"
|
||||
else
|
||||
record_fail "Container status" "no running containers found"
|
||||
fi
|
||||
else
|
||||
record_fail "Container status" "could not query container status"
|
||||
fi
|
||||
|
||||
# 2c. List domains
|
||||
local domains_status
|
||||
domains_status=$(api_curl_status "/api/v1/get/domain/all")
|
||||
if [[ "$domains_status" == "200" ]]; then
|
||||
local domains_json
|
||||
domains_json=$(api_curl "/api/v1/get/domain/all" 2>/dev/null) || domains_json=""
|
||||
local domain_count
|
||||
domain_count=$(echo "$domains_json" | { grep -oP '"domain_name"\s*:' || true; } | wc -l)
|
||||
record_pass "List domains" "${domain_count} domain(s) found"
|
||||
else
|
||||
record_fail "List domains" "HTTP ${domains_status}"
|
||||
fi
|
||||
|
||||
# 2d. List mailboxes
|
||||
local mailboxes_status
|
||||
mailboxes_status=$(api_curl_status "/api/v1/get/mailbox/all")
|
||||
if [[ "$mailboxes_status" == "200" ]]; then
|
||||
local mailboxes_json
|
||||
mailboxes_json=$(api_curl "/api/v1/get/mailbox/all" 2>/dev/null) || mailboxes_json=""
|
||||
local mailbox_count
|
||||
mailbox_count=$(echo "$mailboxes_json" | { grep -oP '"username"\s*:' || true; } | wc -l)
|
||||
record_pass "List mailboxes" "${mailbox_count} mailbox(es) found"
|
||||
else
|
||||
record_fail "List mailboxes" "HTTP ${mailboxes_status}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 3. Mail Services ─────────────────────────────────────────────────
|
||||
test_mail_services() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Mail Services${RESET}"
|
||||
|
||||
local host
|
||||
host=$(get_mailcow_host)
|
||||
|
||||
# 3a. SMTP
|
||||
if [[ "$SKIP_SMTP" == "true" ]]; then
|
||||
record_skip "SMTP port 25" "SKIP_SMTP=true"
|
||||
record_skip "SMTP port 587" "SKIP_SMTP=true"
|
||||
else
|
||||
if check_port "$host" 25; then
|
||||
record_pass "SMTP port 25" "accepting connections"
|
||||
else
|
||||
record_fail "SMTP port 25" "not reachable"
|
||||
fi
|
||||
|
||||
if check_port "$host" 587; then
|
||||
record_pass "SMTP port 587 (submission)" "accepting connections"
|
||||
else
|
||||
record_fail "SMTP port 587 (submission)" "not reachable"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3b. IMAP
|
||||
if [[ "$SKIP_IMAP" == "true" ]]; then
|
||||
record_skip "IMAP port 143" "SKIP_IMAP=true"
|
||||
record_skip "IMAP port 993" "SKIP_IMAP=true"
|
||||
else
|
||||
if check_port "$host" 143; then
|
||||
record_pass "IMAP port 143" "accepting connections"
|
||||
else
|
||||
record_fail "IMAP port 143" "not reachable"
|
||||
fi
|
||||
|
||||
if check_port "$host" 993; then
|
||||
record_pass "IMAP port 993 (IMAPS)" "accepting connections"
|
||||
else
|
||||
record_fail "IMAP port 993 (IMAPS)" "not reachable"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3c. POP3 (optional — skip if not available)
|
||||
if check_port "$host" 110; then
|
||||
record_pass "POP3 port 110" "accepting connections"
|
||||
else
|
||||
record_skip "POP3 port 110" "not reachable (may be disabled)"
|
||||
fi
|
||||
|
||||
if check_port "$host" 995; then
|
||||
record_pass "POP3 port 995 (POP3S)" "accepting connections"
|
||||
else
|
||||
record_skip "POP3 port 995 (POP3S)" "not reachable (may be disabled)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 4. Spam Filter ───────────────────────────────────────────────────
|
||||
test_spam_filter() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Spam Filter${RESET}"
|
||||
|
||||
# 4a. rspamd — check container status from API
|
||||
local containers_json
|
||||
containers_json=$(api_curl "/api/v1/get/status/containers" 2>/dev/null) || containers_json=""
|
||||
|
||||
if [[ -n "$containers_json" ]]; then
|
||||
local rspamd_state
|
||||
rspamd_state=$(echo "$containers_json" | { grep -oP '"rspamd-mailcow[^}]*"state"\s*:\s*"\K[^"]*' || true; } | head -1)
|
||||
|
||||
if [[ -z "$rspamd_state" ]]; then
|
||||
# Try alternate pattern — look for rspamd in container names
|
||||
rspamd_state=$(echo "$containers_json" | { grep -B5 '"rspamd' || true; } | { grep -oP '"state"\s*:\s*"\K[^"]*' || true; } | head -1)
|
||||
fi
|
||||
|
||||
if [[ "$rspamd_state" == "running" ]]; then
|
||||
record_pass "rspamd running" "spam filter operational"
|
||||
elif [[ -n "$rspamd_state" ]]; then
|
||||
record_fail "rspamd running" "state: ${rspamd_state}"
|
||||
else
|
||||
record_skip "rspamd status" "could not determine rspamd container state"
|
||||
fi
|
||||
else
|
||||
record_fail "rspamd status" "could not query containers"
|
||||
fi
|
||||
|
||||
# 4b. ClamAV
|
||||
if [[ "$SKIP_CLAMD" == "true" ]]; then
|
||||
record_skip "ClamAV status" "SKIP_CLAMD=true"
|
||||
elif [[ -n "$containers_json" ]]; then
|
||||
local clamd_state
|
||||
clamd_state=$(echo "$containers_json" | { grep -oP '"clamd-mailcow[^}]*"state"\s*:\s*"\K[^"]*' || true; } | head -1)
|
||||
|
||||
if [[ -z "$clamd_state" ]]; then
|
||||
clamd_state=$(echo "$containers_json" | { grep -B5 '"clamd' || true; } | { grep -oP '"state"\s*:\s*"\K[^"]*' || true; } | head -1)
|
||||
fi
|
||||
|
||||
if [[ "$clamd_state" == "running" ]]; then
|
||||
record_pass "ClamAV running" "antivirus operational"
|
||||
elif [[ -n "$clamd_state" ]]; then
|
||||
record_fail "ClamAV running" "state: ${clamd_state}"
|
||||
else
|
||||
record_skip "ClamAV status" "ClamAV container not found (may be disabled)"
|
||||
fi
|
||||
else
|
||||
record_skip "ClamAV status" "could not query containers"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 5. Webmail ────────────────────────────────────────────────────────
|
||||
test_webmail() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Webmail${RESET}"
|
||||
|
||||
local curl_opts=(-s -o /dev/null -w "%{http_code}" --max-time "$CURL_TIMEOUT")
|
||||
[[ "$CURL_INSECURE" == "true" ]] && curl_opts+=(-k)
|
||||
|
||||
local sogo_code
|
||||
sogo_code=$(curl "${curl_opts[@]}" "${MAILCOW_URL}/SOGo" 2>/dev/null) || sogo_code="000"
|
||||
|
||||
if [[ "$sogo_code" == "200" || "$sogo_code" == "301" || "$sogo_code" == "302" ]]; then
|
||||
record_pass "SOGo reachable" "HTTP ${sogo_code}"
|
||||
elif [[ "$sogo_code" == "000" ]]; then
|
||||
record_fail "SOGo reachable" "connection failed"
|
||||
else
|
||||
record_fail "SOGo reachable" "HTTP ${sogo_code}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 6. Quarantine ────────────────────────────────────────────────────
|
||||
test_quarantine() {
|
||||
echo ""
|
||||
echo -e "${BOLD}Quarantine${RESET}"
|
||||
|
||||
local quarantine_json
|
||||
quarantine_json=$(api_curl "/api/v1/get/quarantine/all" 2>/dev/null) || quarantine_json=""
|
||||
|
||||
local quarantine_status
|
||||
quarantine_status=$(api_curl_status "/api/v1/get/quarantine/all")
|
||||
|
||||
if [[ "$quarantine_status" == "200" ]]; then
|
||||
local q_count
|
||||
q_count=$(echo "$quarantine_json" | { grep -oP '"id"\s*:' || true; } | wc -l)
|
||||
record_pass "Quarantine endpoint" "${q_count} item(s) in quarantine"
|
||||
else
|
||||
record_fail "Quarantine endpoint" "HTTP ${quarantine_status}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# OUTPUT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
print_summary() {
|
||||
local end_time
|
||||
end_time=$(date +%s)
|
||||
local duration=$(( end_time - START_TIME ))
|
||||
|
||||
echo ""
|
||||
echo -e "${BOLD}────────────────────────────────────────${RESET}"
|
||||
echo -e "${BOLD}Summary${RESET} ${MAILCOW_URL}"
|
||||
echo -e " ${GREEN}${PASS} passed${RESET} ${RED}${FAIL} failed${RESET} ${YELLOW}${SKIP} skipped${RESET} (${duration}s)"
|
||||
echo -e "${BOLD}────────────────────────────────────────${RESET}"
|
||||
|
||||
if [[ $FAIL -eq 0 ]]; then
|
||||
echo -e "${GREEN}${BOLD}All tests passed.${RESET}"
|
||||
else
|
||||
echo -e "${RED}${BOLD}${FAIL} test(s) failed.${RESET}"
|
||||
fi
|
||||
}
|
||||
|
||||
print_tap_header() {
|
||||
echo "TAP version 13"
|
||||
}
|
||||
|
||||
print_tap_footer() {
|
||||
echo "1..${TOTAL}"
|
||||
echo "# pass ${PASS}"
|
||||
echo "# fail ${FAIL}"
|
||||
echo "# skip ${SKIP}"
|
||||
}
|
||||
|
||||
write_junit() {
|
||||
local end_time
|
||||
end_time=$(date +%s)
|
||||
local duration=$(( end_time - START_TIME ))
|
||||
|
||||
cat > "$JUNIT_FILE" <<JUNIT_EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuites tests="${TOTAL}" failures="${FAIL}" skipped="${SKIP}" time="${duration}">
|
||||
<testsuite name="mailcow-smoke-tests" tests="${TOTAL}" failures="${FAIL}" skipped="${SKIP}" time="${duration}">
|
||||
JUNIT_EOF
|
||||
|
||||
for result in "${RESULTS[@]}"; do
|
||||
local status name detail
|
||||
status=$(echo "$result" | cut -d'|' -f1)
|
||||
name=$(echo "$result" | cut -d'|' -f2)
|
||||
detail=$(echo "$result" | cut -d'|' -f3)
|
||||
|
||||
# XML-escape the values
|
||||
name=$(echo "$name" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g')
|
||||
detail=$(echo "$detail" | sed 's/&/\&/g; s/</\</g; s/>/\>/g; s/"/\"/g')
|
||||
|
||||
case "$status" in
|
||||
PASS)
|
||||
echo " <testcase name=\"${name}\" classname=\"smoke\">" >> "$JUNIT_FILE"
|
||||
[[ -n "$detail" ]] && echo " <system-out>${detail}</system-out>" >> "$JUNIT_FILE"
|
||||
echo " </testcase>" >> "$JUNIT_FILE"
|
||||
;;
|
||||
FAIL)
|
||||
echo " <testcase name=\"${name}\" classname=\"smoke\">" >> "$JUNIT_FILE"
|
||||
echo " <failure message=\"${detail}\">FAILED: ${name} — ${detail}</failure>" >> "$JUNIT_FILE"
|
||||
echo " </testcase>" >> "$JUNIT_FILE"
|
||||
;;
|
||||
SKIP)
|
||||
echo " <testcase name=\"${name}\" classname=\"smoke\">" >> "$JUNIT_FILE"
|
||||
echo " <skipped message=\"${detail}\"/>" >> "$JUNIT_FILE"
|
||||
echo " </testcase>" >> "$JUNIT_FILE"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo " </testsuite>" >> "$JUNIT_FILE"
|
||||
echo "</testsuites>" >> "$JUNIT_FILE"
|
||||
|
||||
log "JUnit report written to ${JUNIT_FILE}"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Smoke-test a Mailcow instance. Validates API, containers, mail services, spam
|
||||
filter, webmail, and quarantine. Requires: bash 4+, curl, openssl, nc.
|
||||
|
||||
Required environment variables:
|
||||
MAILCOW_URL Mailcow base URL (https://mail.example.com)
|
||||
MAILCOW_API_KEY API key (read-only is sufficient)
|
||||
|
||||
Optional environment variables:
|
||||
MAILCOW_DIR Mailcow install directory (default: /opt/mailcow-dockerized)
|
||||
CURL_TIMEOUT HTTP timeout in seconds (default: 10)
|
||||
SKIP_SMTP Skip SMTP port checks (default: false)
|
||||
SKIP_IMAP Skip IMAP port checks (default: false)
|
||||
SKIP_CLAMD Skip ClamAV checks (default: false)
|
||||
|
||||
Options:
|
||||
--skip-smtp Skip SMTP port tests
|
||||
--skip-imap Skip IMAP port tests
|
||||
--insecure Allow self-signed TLS certificates (-k)
|
||||
--timeout N curl timeout in seconds (default: 10)
|
||||
--format FORMAT Output: text (default), tap, junit
|
||||
--junit-file FILE JUnit output path (default: smoke-results.xml)
|
||||
--verbose Show debug output
|
||||
--no-color Disable colored output
|
||||
--help Show this help
|
||||
|
||||
Examples:
|
||||
# Basic run
|
||||
export MAILCOW_URL="https://mail.example.com"
|
||||
export MAILCOW_API_KEY="your-api-key"
|
||||
./$(basename "$0")
|
||||
|
||||
# Self-signed cert, JUnit output
|
||||
MAILCOW_URL=https://mail.local MAILCOW_API_KEY=xxx \\
|
||||
./$(basename "$0") --insecure --format junit
|
||||
|
||||
# Skip port checks (remote host)
|
||||
MAILCOW_URL=https://mail.local MAILCOW_API_KEY=xxx \\
|
||||
./$(basename "$0") --skip-smtp --skip-imap
|
||||
|
||||
# TAP output for CI pipeline
|
||||
MAILCOW_URL=https://mail.local MAILCOW_API_KEY=xxx \\
|
||||
./$(basename "$0") --format tap
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skip-smtp) SKIP_SMTP=true ;;
|
||||
--skip-imap) SKIP_IMAP=true ;;
|
||||
--insecure) CURL_INSECURE=true ;;
|
||||
--timeout) CURL_TIMEOUT="$2"; shift ;;
|
||||
--format) OUTPUT_FORMAT="$2"; shift ;;
|
||||
--junit-file) JUNIT_FILE="$2"; shift ;;
|
||||
--verbose) VERBOSE=true ;;
|
||||
--no-color) COLOR=never ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) err "Unknown option: $1"; usage; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
setup_colors
|
||||
|
||||
# Validate required vars
|
||||
if [[ -z "$MAILCOW_URL" ]]; then
|
||||
err "MAILCOW_URL is required"
|
||||
echo ""
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$MAILCOW_API_KEY" ]]; then
|
||||
err "MAILCOW_API_KEY is required"
|
||||
echo ""
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Strip trailing slash
|
||||
MAILCOW_URL="${MAILCOW_URL%/}"
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
print_tap_header
|
||||
else
|
||||
echo ""
|
||||
echo -e "${BOLD}Mailcow Smoke Tests${RESET}"
|
||||
echo -e "Target: ${MAILCOW_URL}"
|
||||
echo -e "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Run test suites
|
||||
test_connectivity
|
||||
test_api
|
||||
test_mail_services
|
||||
test_spam_filter
|
||||
test_webmail
|
||||
test_quarantine
|
||||
|
||||
# Output
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
print_tap_footer
|
||||
elif [[ "$OUTPUT_FORMAT" == "junit" ]]; then
|
||||
print_summary
|
||||
write_junit
|
||||
else
|
||||
print_summary
|
||||
fi
|
||||
|
||||
# Exit code
|
||||
[[ $FAIL -eq 0 ]] && exit 0 || exit 1
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user