#!/bin/bash # Bitcoin Knots health check — managed by Ansible (roles/bitcoin_knots) # # The exit code is the answer and systemd keeps it: # systemctl is-failed bitcoin-knots-healthcheck.service # Reporting anywhere else is optional and generic. # # RPC_HOST="{{ bitcoin_rpc_bind }}" RPC_PORT={{ bitcoin_rpc_port }} RPC_USER="{{ bitcoin_rpc_user }}" RPC_PASSWORD="{{ bitcoin_rpc_password }}" PUSH_URL="${HEALTHCHECK_PUSH_URL:-}" PUSH_TOKEN="${HEALTHCHECK_PUSH_TOKEN:-}" # Check if bitcoind RPC is responding check_bitcoind() { local response response=$(curl -s --max-time 30 \ --user "${RPC_USER}:${RPC_PASSWORD}" \ --data-binary '{"jsonrpc":"1.0","id":"healthcheck","method":"getblockchaininfo","params":[]}' \ --header 'Content-Type: application/json' \ "http://${RPC_HOST}:${RPC_PORT}" 2>&1) if [ $? -eq 0 ]; then # Check if response contains a non-null error # Successful responses have "error": null, failures have "error": {...} if echo "$response" | grep -q '"error":null\|"error": null'; then return 0 else return 1 fi else return 1 fi } report() { local status=$1 local msg=$2 # No push URL is normal, not an error: the exit code below is still a # complete answer for anything reading unit state. [ -n "$PUSH_URL" ] || return 0 # URL encode spaces in message local encoded_msg="${msg// /%20}" # Gatus external endpoint: a POST with a bearer token, NOT Uptime Kuma's # GET with ?status=up. The callers still pass up/down, so the mapping is # done here rather than at every call site. local _ok=false [ "${status}" = "up" ] && _ok=true if ! curl -s --max-time 15 --retry 2 -o /dev/null -X POST \ -H "Authorization: Bearer ${PUSH_TOKEN}" \ "${PUSH_URL}?success=${_ok}&error=${encoded_msg}"; then return 1 fi } # Main health check if check_bitcoind; then report "up" "OK" exit 0 else report "down" "bitcoind RPC not responding" exit 1 fi