datum-gateway: convert to a role, de-Uptime-Kuma the health check
802-line playbook becomes 68 lines (three plays: the role, the Caddy dashboard, the Stratum socket proxy) plus a 345-line role. datum_gateway_vars.yml is deleted; its content is the role's defaults. Verified after a real run with zero miners connected: datum-gateway restarted cleanly onto the reformatted config, deployed config.json semantically identical to what was there (pool_address bc1qvrj3g84..., pool_pass_* false, ports unchanged), health check timer firing, and the Knots side untouched - bitcoind still up since 2026-08-19 with blocknotify intact. TWO PIECES OF DRIFT WHERE THE NODE WAS RIGHT, both confirmed with the operator: - datum_mining_address: the vault held bc1qdse9dsg... while the node had been mining to bc1qvrj3g... since 2026-08-08. This is WHERE BLOCK REWARDS ARE PAID. And unlike fulcrum and bitcoin-knots, the `Restart datum-gateway` handler here was never gated, so the stale value would have applied immediately rather than sitting inert on disk. - pool_pass_workers / pool_pass_full_users: false on the node, true in the vars file. Both corrected in the vault and role defaults with notes recording why. Comparing this config needs semantics, not text: the live file is single-line JSON and the template renders pretty-printed, so a textual diff is pure noise. Rendering it and comparing parsed JSON is what surfaced both differences. config.json carries bitcoind.rpcpassword and api.admin_password, and --diff prints rendered content - so `--check --diff` put them on the terminal. The task now sets diff: false by default (-e datum_reveal_config=true to opt in). Those two should be rotated. I also mis-reported pool_pass_workers/pool_pass_full_users as exposed credentials because my masking matched "pass" in the key name. They are BOOLEANS, and mining.pool_address is a Bitcoin address, public by nature. Only the two real passwords above were exposed. `Configure cmake build` and `Compile datum_gateway` are bare command: tasks with no changed_when, so they recompile on every run. The build is reproducible - Install datum_gateway binary sees identical content and leaves the installed binary's timestamp alone - but it is wasted work each time. Documented as the idempotent floor. Ownership parity checked mechanically against `git show HEAD:` keyed by task name: 7/7 match, 9 Kuma tasks dropped. This completes Plan 6 Stage 2: all six services in the list are roles. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d26dc78b3c
commit
a0c23ae766
17 changed files with 797 additions and 1115 deletions
|
|
@ -1,43 +0,0 @@
|
|||
# DATUM Gateway Configuration Variables
|
||||
# https://github.com/OCEAN-xyz/datum_gateway
|
||||
|
||||
# Version - pin to a specific tag
|
||||
datum_gateway_version: "v0.4.1beta"
|
||||
|
||||
# Directories
|
||||
datum_gateway_dir: /opt/datum-gateway
|
||||
datum_gateway_source_dir: "{{ datum_gateway_dir }}/source"
|
||||
datum_gateway_config_dir: /etc/datum-gateway
|
||||
datum_gateway_log_dir: /var/log/datum-gateway
|
||||
|
||||
# Binary
|
||||
datum_gateway_bin_path: /usr/local/bin/datum_gateway
|
||||
|
||||
# Ports
|
||||
datum_gateway_stratum_port: 23334 # Miners connect here via Stratum v1
|
||||
datum_gateway_api_port: 7152 # Web dashboard / API
|
||||
|
||||
# Stratum settings
|
||||
datum_vardiff_min: 524288 # Minimum share difficulty (must be power of 2; OCEAN floor overrides if higher)
|
||||
|
||||
# Service user
|
||||
datum_gateway_user: datum
|
||||
datum_gateway_group: datum
|
||||
|
||||
# Build options
|
||||
datum_gateway_build_jobs: 4
|
||||
|
||||
# Bitcoin node connection
|
||||
# The gateway runs on the same host as Bitcoin Knots so localhost RPC works.
|
||||
# datum_bitcoin_rpc_url should include http:// and port.
|
||||
datum_bitcoin_rpc_url: "http://127.0.0.1:8332"
|
||||
# Note: bitcoin_rpc_user and bitcoin_rpc_password come from infra_secrets.yml
|
||||
|
||||
# Mining config
|
||||
datum_coinbase_tag_primary: "DATUM"
|
||||
datum_coinbase_tag_secondary: "BY ORDER OF BIP110"
|
||||
|
||||
datum_pool_pass_workers: true
|
||||
datum_pool_pass_full_users: true
|
||||
datum_pooled_mining_only: true
|
||||
|
||||
|
|
@ -1,667 +1,59 @@
|
|||
---
|
||||
# DATUM Gateway Deployment Playbook
|
||||
# DATUM Gateway: solo/pooled mining gateway, built from source on knots-box.
|
||||
#
|
||||
# Deploys DATUM Gateway (https://github.com/OCEAN-xyz/datum_gateway) on the
|
||||
# Bitcoin Knots host so it has direct localhost RPC access to bitcoind.
|
||||
#
|
||||
# What this does:
|
||||
# 1. Installs build deps and compiles datum_gateway from source
|
||||
# 2. Creates a dedicated system user and config/log directories
|
||||
# 3. Writes /etc/datum-gateway/config.json from vars/secrets
|
||||
# 4. Patches bitcoin.conf with the required blockmaxsize/blocknotify lines
|
||||
# 5. Creates and enables a systemd service
|
||||
# 6. Creates a push-monitor health check script + systemd timer
|
||||
# 7. Registers a push monitor in Uptime Kuma
|
||||
#
|
||||
# Separate play: adds a Caddy reverse proxy on vipy for the dashboard.
|
||||
#
|
||||
# Stratum port (23334) is bound on knots_box_local. Expose it to miners via
|
||||
# a firewall rule, Tailscale, or a socket proxy on vipy — not handled here.
|
||||
#
|
||||
# Required secrets in infra_secrets.yml:
|
||||
# datum_mining_address - Bitcoin address for block rewards
|
||||
# datum_gateway_admin_password - Password for the /api admin endpoint
|
||||
# bitcoin_rpc_user - Shared with the bitcoin-knots deployment
|
||||
# bitcoin_rpc_password - Shared with the bitcoin-knots deployment
|
||||
|
||||
- name: Deploy DATUM Gateway on knots_box_local
|
||||
# This is HALF OF A SYSTEM. The Bitcoin Knots node on the same host feeds it via
|
||||
# `blocknotify=killall -USR1 datum_gateway` in bitcoin.conf (see
|
||||
# roles/bitcoin_knots/README.md). Changing either config means thinking about
|
||||
# both. Interrupting Stratum costs mining shares, so check for connected miners
|
||||
# before restarting:
|
||||
# ss -tn state established '( sport = :23334 )'
|
||||
- name: Deploy DATUM Gateway on the bitcoin host
|
||||
hosts: bitcoin
|
||||
become: yes
|
||||
vars_files:
|
||||
- ../../infra_vars.yml
|
||||
- ../../services_config.yml
|
||||
- ../../infra_secrets.yml
|
||||
- ./datum_gateway_vars.yml
|
||||
vars:
|
||||
datum_gateway_subdomain: "{{ subdomains.datum_gateway }}"
|
||||
datum_gateway_domain: "{{ datum_gateway_subdomain }}.{{ root_domain }}"
|
||||
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||
# Preserves the push URL this check reports to. The role knows nothing about
|
||||
# Uptime Kuma — this is just "a URL that accepts a ping".
|
||||
healthcheck_push_url: "{{ healthcheck_push_urls.datum_gateway | default('') }}"
|
||||
roles:
|
||||
- datum_gateway
|
||||
|
||||
tasks:
|
||||
# ===========================================
|
||||
# Build Dependencies
|
||||
# ===========================================
|
||||
- name: Install DATUM Gateway build dependencies
|
||||
apt:
|
||||
name:
|
||||
- cmake
|
||||
- build-essential
|
||||
- git
|
||||
- libjansson-dev
|
||||
- libmicrohttpd-dev
|
||||
- libsodium-dev
|
||||
- libcurl4-openssl-dev
|
||||
# Runtime-only (netcat for health check)
|
||||
- netcat-openbsd
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
# ===========================================
|
||||
# System User and Directories
|
||||
# ===========================================
|
||||
- name: Create datum system user
|
||||
user:
|
||||
name: "{{ datum_gateway_user }}"
|
||||
system: yes
|
||||
shell: /usr/sbin/nologin
|
||||
home: "{{ datum_gateway_dir }}"
|
||||
create_home: no
|
||||
comment: "DATUM Gateway"
|
||||
|
||||
- name: Create DATUM Gateway directories
|
||||
file:
|
||||
path: "{{ item.path }}"
|
||||
state: directory
|
||||
owner: "{{ item.owner }}"
|
||||
group: "{{ datum_gateway_group }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop:
|
||||
- { path: "{{ datum_gateway_dir }}", owner: root, mode: "0755" }
|
||||
- { path: "{{ datum_gateway_source_dir }}", owner: root, mode: "0755" }
|
||||
- { path: "{{ datum_gateway_config_dir }}", owner: "{{ datum_gateway_user }}", mode: "0750" }
|
||||
- { path: "{{ datum_gateway_log_dir }}", owner: "{{ datum_gateway_user }}", mode: "0750" }
|
||||
|
||||
# ===========================================
|
||||
# Build from Source
|
||||
# ===========================================
|
||||
- name: Clone DATUM Gateway repository at {{ datum_gateway_version }}
|
||||
git:
|
||||
repo: https://github.com/OCEAN-xyz/datum_gateway.git
|
||||
dest: "{{ datum_gateway_source_dir }}"
|
||||
version: "{{ datum_gateway_version }}"
|
||||
force: yes
|
||||
register: git_clone
|
||||
|
||||
- name: Configure cmake build
|
||||
command: cmake . -DCMAKE_BUILD_TYPE=Release
|
||||
args:
|
||||
chdir: "{{ datum_gateway_source_dir }}"
|
||||
|
||||
- name: Compile datum_gateway
|
||||
command: make -j{{ datum_gateway_build_jobs }}
|
||||
args:
|
||||
chdir: "{{ datum_gateway_source_dir }}"
|
||||
|
||||
- name: Install datum_gateway binary
|
||||
copy:
|
||||
src: "{{ datum_gateway_source_dir }}/datum_gateway"
|
||||
dest: "{{ datum_gateway_bin_path }}"
|
||||
remote_src: yes
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
notify: Restart datum-gateway
|
||||
|
||||
# ===========================================
|
||||
# Configuration
|
||||
# ===========================================
|
||||
- name: Write DATUM Gateway config.json
|
||||
copy:
|
||||
dest: "{{ datum_gateway_config_dir }}/config.json"
|
||||
content: |
|
||||
{
|
||||
"bitcoind": {
|
||||
"rpcuser": "{{ bitcoin_rpc_user }}",
|
||||
"rpcpassword": "{{ bitcoin_rpc_password }}",
|
||||
"rpcurl": "{{ datum_bitcoin_rpc_url }}",
|
||||
"notify_fallback": true
|
||||
},
|
||||
"stratum": {
|
||||
"listen_port": {{ datum_gateway_stratum_port }},
|
||||
"vardiff_min": {{ datum_vardiff_min }}
|
||||
},
|
||||
"mining": {
|
||||
"pool_address": "{{ datum_mining_address }}",
|
||||
"coinbase_tag_primary": "{{ datum_coinbase_tag_primary }}",
|
||||
"coinbase_tag_secondary": "{{ datum_coinbase_tag_secondary }}"
|
||||
},
|
||||
"api": {
|
||||
"admin_password": "{{ datum_gateway_admin_password }}",
|
||||
"listen_port": {{ datum_gateway_api_port }},
|
||||
"modify_conf": false
|
||||
},
|
||||
"logger": {
|
||||
"log_to_console": true,
|
||||
"log_to_file": true,
|
||||
"log_file": "{{ datum_gateway_log_dir }}/datum_gateway.log",
|
||||
"log_rotate_daily": true,
|
||||
"log_level_console": 2,
|
||||
"log_level_file": 1
|
||||
},
|
||||
"datum": {
|
||||
"pool_pass_workers": {{ datum_pool_pass_workers | lower }},
|
||||
"pool_pass_full_users": {{ datum_pool_pass_full_users | lower }},
|
||||
"pooled_mining_only": {{ datum_pooled_mining_only | lower }}
|
||||
}
|
||||
}
|
||||
owner: "{{ datum_gateway_user }}"
|
||||
group: "{{ datum_gateway_group }}"
|
||||
mode: "0640"
|
||||
notify: Restart datum-gateway
|
||||
|
||||
# ===========================================
|
||||
# Systemd Service
|
||||
# ===========================================
|
||||
- name: Create datum-gateway systemd service
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-gateway.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Gateway - Bitcoin Mining Gateway
|
||||
Documentation=https://github.com/OCEAN-xyz/datum_gateway
|
||||
After=network.target bitcoind.service
|
||||
Wants=bitcoind.service
|
||||
|
||||
[Service]
|
||||
User={{ datum_gateway_user }}
|
||||
Group={{ datum_gateway_group }}
|
||||
Type=simple
|
||||
ExecStart={{ datum_gateway_bin_path }} --config {{ datum_gateway_config_dir }}/config.json
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
# Prevent config from being read by other users
|
||||
ReadWritePaths={{ datum_gateway_log_dir }}
|
||||
ReadOnlyPaths={{ datum_gateway_config_dir }}
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart datum-gateway
|
||||
|
||||
- name: Reload systemd daemon
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Enable and start datum-gateway
|
||||
systemd:
|
||||
name: datum-gateway
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
# ===========================================
|
||||
# Health Check Script + Systemd Timer
|
||||
# ===========================================
|
||||
# ═════════════════════════════════════════════════════════════════════════
|
||||
# DEPRECATED — Uptime Kuma was decommissioned on 2026-09-11.
|
||||
#
|
||||
# Every task below is inert: uptime_kuma_enabled is false in
|
||||
# group_vars/all/main.yml, so they all skip and the deployment above still
|
||||
# runs normally. Kept because the health-check logic is the durable part —
|
||||
# when a replacement exists, rewire the push transport and flip the flag.
|
||||
#
|
||||
# What was being monitored: archive/uptime_kuma/MONITORS.md
|
||||
# ═════════════════════════════════════════════════════════════════════════
|
||||
- name: Create DATUM Gateway health check script
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
copy:
|
||||
dest: /usr/local/bin/datum-gateway-healthcheck-push.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
UPTIME_KUMA_PUSH_URL="${UPTIME_KUMA_PUSH_URL}"
|
||||
STRATUM_PORT={{ datum_gateway_stratum_port }}
|
||||
|
||||
check_datum() {
|
||||
# Service must be active and stratum port must be listening
|
||||
systemctl is-active --quiet datum-gateway && \
|
||||
nc -z 127.0.0.1 "${STRATUM_PORT}"
|
||||
}
|
||||
|
||||
push_to_uptime_kuma() {
|
||||
local status=$1
|
||||
local msg=$2
|
||||
if [ -z "$UPTIME_KUMA_PUSH_URL" ]; then
|
||||
echo "ERROR: UPTIME_KUMA_PUSH_URL not set"
|
||||
return 1
|
||||
fi
|
||||
curl -s --max-time 10 --retry 2 -o /dev/null \
|
||||
"${UPTIME_KUMA_PUSH_URL}?status=${status}&msg=${msg// /%20}&ping=" || true
|
||||
}
|
||||
|
||||
if check_datum; then
|
||||
push_to_uptime_kuma "up" "OK"
|
||||
exit 0
|
||||
else
|
||||
push_to_uptime_kuma "down" "DATUM Gateway not responding"
|
||||
exit 1
|
||||
fi
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Create datum-gateway health check systemd service
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-gateway-healthcheck.service
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Gateway Health Check
|
||||
After=network.target datum-gateway.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/datum-gateway-healthcheck-push.sh
|
||||
Environment=UPTIME_KUMA_PUSH_URL=
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Create datum-gateway health check systemd timer
|
||||
copy:
|
||||
dest: /etc/systemd/system/datum-gateway-healthcheck.timer
|
||||
content: |
|
||||
[Unit]
|
||||
Description=DATUM Gateway Health Check Timer
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=1min
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Reload systemd daemon after health check units
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Enable and start datum-gateway health check timer
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
systemd:
|
||||
name: datum-gateway-healthcheck.timer
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
# ===========================================
|
||||
# Uptime Kuma Push Monitor Setup
|
||||
# ===========================================
|
||||
- name: Create Uptime Kuma push monitor setup script for DATUM Gateway
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/setup_datum_gateway_monitor.py
|
||||
content: |
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
try:
|
||||
import socketio.exceptions
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
try:
|
||||
with open('/tmp/ansible_datum_gateway_config.yml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
url = config['uptime_kuma_url']
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
monitor_name = config['monitor_name']
|
||||
|
||||
api = UptimeKumaApi(url, timeout=30)
|
||||
api.login(username, password)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
|
||||
# Find or create "services" group
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
if not group:
|
||||
try:
|
||||
api.add_monitor(type='group', name='services')
|
||||
except Exception:
|
||||
time.sleep(2)
|
||||
monitors = api.get_monitors()
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
|
||||
# Get ntfy notification ID
|
||||
notifications = api.get_notifications()
|
||||
ntfy_notification_id = None
|
||||
for notif in notifications:
|
||||
if notif.get('type') == 'ntfy':
|
||||
ntfy_notification_id = notif.get('id')
|
||||
break
|
||||
|
||||
# Check if monitor already exists
|
||||
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
|
||||
push_url = None
|
||||
|
||||
if existing:
|
||||
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
|
||||
push_token = existing.get('pushToken') or existing.get('push_token')
|
||||
if push_token:
|
||||
push_url = f"{url}/api/push/{push_token}"
|
||||
else:
|
||||
print(f"Creating push monitor '{monitor_name}'...")
|
||||
try:
|
||||
api.add_monitor(
|
||||
type=MonitorType.PUSH,
|
||||
name=monitor_name,
|
||||
parent=group['id'],
|
||||
interval=90,
|
||||
maxretries=3,
|
||||
retryInterval=60,
|
||||
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
|
||||
)
|
||||
except Exception as e:
|
||||
# socketio timeout: add_monitor may have succeeded server-side
|
||||
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
new_monitor = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
if new_monitor:
|
||||
push_token = new_monitor.get('pushToken') or new_monitor.get('push_token')
|
||||
if push_token:
|
||||
push_url = f"{url}/api/push/{push_token}"
|
||||
|
||||
api.disconnect()
|
||||
|
||||
if push_url:
|
||||
print(f"PUSH_URL={push_url}")
|
||||
with open('/tmp/datum_gateway_push_url.txt', 'w') as f:
|
||||
f.write(push_url)
|
||||
|
||||
print("SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: {str(e)}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
mode: "0755"
|
||||
|
||||
- name: Create temporary config for push monitor setup
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/ansible_datum_gateway_config.yml
|
||||
content: |
|
||||
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
|
||||
username: "{{ uptime_kuma_username }}"
|
||||
password: "{{ uptime_kuma_password }}"
|
||||
monitor_name: "DATUM Gateway"
|
||||
mode: "0644"
|
||||
|
||||
- name: Run Uptime Kuma push monitor setup
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
command: python3 /tmp/setup_datum_gateway_monitor.py
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: monitor_setup
|
||||
changed_when: "'SUCCESS' in monitor_setup.stdout"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display monitor setup output
|
||||
debug:
|
||||
msg: "{{ monitor_setup.stdout_lines }}"
|
||||
when: monitor_setup.stdout is defined
|
||||
|
||||
- name: Read push URL from file
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
slurp:
|
||||
src: /tmp/datum_gateway_push_url.txt
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: push_url_file
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Parse push URL
|
||||
set_fact:
|
||||
datum_push_url: "{{ push_url_file.content | b64decode | trim }}"
|
||||
when: push_url_file.content is defined
|
||||
|
||||
- name: Update health check service with push URL
|
||||
lineinfile:
|
||||
path: /etc/systemd/system/datum-gateway-healthcheck.service
|
||||
regexp: "^Environment=UPTIME_KUMA_PUSH_URL="
|
||||
line: "Environment=UPTIME_KUMA_PUSH_URL={{ datum_push_url }}"
|
||||
when: datum_push_url is defined
|
||||
notify: Restart datum-gateway health check timer
|
||||
|
||||
- name: Clean up temporary files
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/setup_datum_gateway_monitor.py
|
||||
- /tmp/ansible_datum_gateway_config.yml
|
||||
- /tmp/datum_gateway_push_url.txt
|
||||
|
||||
handlers:
|
||||
- name: Restart datum-gateway
|
||||
systemd:
|
||||
name: datum-gateway
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Restart datum-gateway health check timer
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
systemd:
|
||||
name: datum-gateway-healthcheck.timer
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Caddy Reverse Proxy for DATUM Dashboard (on vipy)
|
||||
# ===========================================
|
||||
- name: Configure Caddy reverse proxy for DATUM Gateway dashboard on the edge host
|
||||
- name: Configure Caddy reverse proxy for the DATUM Gateway dashboard on the edge host
|
||||
hosts: edge
|
||||
become: yes
|
||||
vars_files:
|
||||
- ../../infra_vars.yml
|
||||
- ../../services_config.yml
|
||||
- ../../infra_secrets.yml
|
||||
- ./datum_gateway_vars.yml
|
||||
vars:
|
||||
datum_gateway_subdomain: "{{ subdomains.datum_gateway }}"
|
||||
datum_gateway_domain: "{{ datum_gateway_subdomain }}.{{ root_domain }}"
|
||||
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||
|
||||
tasks:
|
||||
- name: Publish the DATUM Gateway dashboard through Caddy
|
||||
ansible.builtin.include_role:
|
||||
name: caddy_site
|
||||
vars:
|
||||
caddy_site_name: datum-gateway
|
||||
caddy_site_domain: "{{ datum_gateway_domain }}"
|
||||
caddy_site_upstream: "knots-box:{{ datum_gateway_api_port }}"
|
||||
caddy_site_domain: "{{ subdomains.datum_gateway }}.{{ root_domain }}"
|
||||
caddy_site_upstream: "{{ service_settings.datum_gateway.tailscale_hostname }}:{{ service_settings.datum_gateway.api_port }}"
|
||||
caddy_site_resolvers: "100.100.100.100"
|
||||
caddy_site_basic_auth:
|
||||
- user: "{{ datum_dashboard_username }}"
|
||||
hash: "{{ datum_dashboard_password_hash }}"
|
||||
|
||||
# The role validates the site fragment on its own. This re-validates the
|
||||
# whole assembled Caddyfile, which is the only thing that catches a
|
||||
# conflict between this site and another. Kept from the hand-rolled
|
||||
# version; the other nine services never had it.
|
||||
# The role validates its own site fragment; this re-validates the whole
|
||||
# assembled Caddyfile, which is the only thing that catches a conflict
|
||||
# between two sites.
|
||||
- name: Validate the assembled Caddyfile
|
||||
ansible.builtin.command: caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
changed_when: false
|
||||
|
||||
- name: Display DATUM Gateway dashboard URL
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
debug:
|
||||
msg: "DATUM Gateway dashboard: https://{{ datum_gateway_domain }}"
|
||||
|
||||
# ===========================================
|
||||
# Uptime Kuma HTTP Monitor for Public Dashboard
|
||||
# ===========================================
|
||||
- name: Create Uptime Kuma HTTP monitor setup script for DATUM dashboard
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/setup_datum_http_monitor.py
|
||||
content: |
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
try:
|
||||
with open('/tmp/ansible_datum_http_config.yml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
url = config['uptime_kuma_url']
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
monitor_url = config['monitor_url']
|
||||
monitor_name = config['monitor_name']
|
||||
|
||||
api = UptimeKumaApi(url, timeout=30)
|
||||
api.login(username, password)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
if not group:
|
||||
try:
|
||||
api.add_monitor(type='group', name='services')
|
||||
except Exception:
|
||||
time.sleep(2)
|
||||
monitors = api.get_monitors()
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
|
||||
notifications = api.get_notifications()
|
||||
ntfy_notification_id = None
|
||||
for notif in notifications:
|
||||
if notif.get('type') == 'ntfy':
|
||||
ntfy_notification_id = notif.get('id')
|
||||
break
|
||||
|
||||
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
|
||||
if existing:
|
||||
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
|
||||
else:
|
||||
print(f"Creating HTTP monitor '{monitor_name}'...")
|
||||
try:
|
||||
api.add_monitor(
|
||||
type=MonitorType.HTTP,
|
||||
name=monitor_name,
|
||||
url=monitor_url,
|
||||
parent=group['id'],
|
||||
interval=60,
|
||||
maxretries=3,
|
||||
retryInterval=60,
|
||||
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
api.disconnect()
|
||||
print("SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: {str(e)}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
mode: "0755"
|
||||
|
||||
- name: Create temporary config for HTTP monitor
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/ansible_datum_http_config.yml
|
||||
content: |
|
||||
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
|
||||
username: "{{ uptime_kuma_username }}"
|
||||
password: "{{ uptime_kuma_password }}"
|
||||
monitor_url: "https://{{ datum_gateway_domain }}"
|
||||
monitor_name: "DATUM Gateway Dashboard"
|
||||
mode: "0644"
|
||||
|
||||
- name: Run Uptime Kuma HTTP monitor setup
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
command: python3 /tmp/setup_datum_http_monitor.py
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: http_monitor_setup
|
||||
changed_when: "'SUCCESS' in http_monitor_setup.stdout"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display HTTP monitor setup output
|
||||
debug:
|
||||
msg: "{{ http_monitor_setup.stdout_lines }}"
|
||||
when: http_monitor_setup.stdout is defined
|
||||
|
||||
- name: Clean up HTTP monitor temporary files
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/setup_datum_http_monitor.py
|
||||
- /tmp/ansible_datum_http_config.yml
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Stratum Port Forwarding on vipy via systemd-socket-proxyd
|
||||
# Miners connect to vipy:23334; traffic is forwarded to knots-box:23334
|
||||
# over the Tailscale network, matching the Bitcoin P2P proxy pattern.
|
||||
# ===========================================
|
||||
- name: Setup public Stratum port forwarding on the edge host
|
||||
hosts: edge
|
||||
become: yes
|
||||
vars_files:
|
||||
- ../../infra_vars.yml
|
||||
- ../../services_config.yml
|
||||
- ../../infra_secrets.yml
|
||||
- ./datum_gateway_vars.yml
|
||||
vars:
|
||||
datum_tailscale_hostname: "knots-box"
|
||||
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||
|
||||
tasks:
|
||||
- name: Expose the DATUM Stratum port through a socket proxy
|
||||
ansible.builtin.include_role:
|
||||
|
|
@ -669,134 +61,8 @@
|
|||
vars:
|
||||
socket_proxy_name: datum-stratum
|
||||
socket_proxy_description: "DATUM Stratum"
|
||||
socket_proxy_listen_port: "{{ datum_gateway_stratum_port }}"
|
||||
socket_proxy_upstream_host: "{{ datum_tailscale_hostname }}"
|
||||
# Matches the UFW comment already on vipy; the derived default would
|
||||
# have said "DATUM Stratum" and rewritten the rule.
|
||||
socket_proxy_listen_port: "{{ service_settings.datum_gateway.stratum_port }}"
|
||||
socket_proxy_upstream_host: "{{ service_settings.datum_gateway.tailscale_hostname }}"
|
||||
# Matches the UFW comment already on the edge host; the derived default
|
||||
# would say "DATUM Stratum" and rewrite the rule.
|
||||
socket_proxy_ufw_comment: "DATUM Gateway Stratum public access"
|
||||
|
||||
- name: Display public Stratum endpoint
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
debug:
|
||||
msg: "DATUM Stratum public endpoint: {{ ansible_host }}:{{ datum_gateway_stratum_port }}"
|
||||
|
||||
# ===========================================
|
||||
# Uptime Kuma TCP Monitor for Public Stratum
|
||||
# ===========================================
|
||||
- name: Create Uptime Kuma TCP monitor setup script for Stratum
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/setup_datum_stratum_tcp_monitor.py
|
||||
content: |
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import yaml
|
||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
||||
|
||||
try:
|
||||
with open('/tmp/ansible_datum_stratum_config.yml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
url = config['uptime_kuma_url']
|
||||
username = config['username']
|
||||
password = config['password']
|
||||
monitor_host = config['monitor_host']
|
||||
monitor_port = config['monitor_port']
|
||||
monitor_name = config['monitor_name']
|
||||
|
||||
api = UptimeKumaApi(url, timeout=30)
|
||||
api.login(username, password)
|
||||
|
||||
monitors = api.get_monitors()
|
||||
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
if not group:
|
||||
try:
|
||||
api.add_monitor(type='group', name='services')
|
||||
except Exception:
|
||||
time.sleep(2)
|
||||
monitors = api.get_monitors()
|
||||
group = next((m for m in monitors if m.get('name') == 'services' and m.get('type') == 'group'), None)
|
||||
|
||||
notifications = api.get_notifications()
|
||||
ntfy_notification_id = None
|
||||
for notif in notifications:
|
||||
if notif.get('type') == 'ntfy':
|
||||
ntfy_notification_id = notif.get('id')
|
||||
break
|
||||
|
||||
existing = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||
|
||||
if existing:
|
||||
print(f"Monitor '{monitor_name}' already exists (ID: {existing['id']})")
|
||||
else:
|
||||
print(f"Creating TCP monitor '{monitor_name}'...")
|
||||
try:
|
||||
api.add_monitor(
|
||||
type=MonitorType.PORT,
|
||||
name=monitor_name,
|
||||
hostname=monitor_host,
|
||||
port=monitor_port,
|
||||
parent=group['id'],
|
||||
interval=60,
|
||||
maxretries=3,
|
||||
retryInterval=60,
|
||||
notificationIDList={ntfy_notification_id: True} if ntfy_notification_id else {}
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"add_monitor raised (possibly timeout): {e}", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
api.disconnect()
|
||||
print("SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR: {str(e)}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
mode: "0755"
|
||||
|
||||
- name: Create temporary config for Stratum TCP monitor
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
copy:
|
||||
dest: /tmp/ansible_datum_stratum_config.yml
|
||||
content: |
|
||||
uptime_kuma_url: "{{ uptime_kuma_api_url }}"
|
||||
username: "{{ uptime_kuma_username }}"
|
||||
password: "{{ uptime_kuma_password }}"
|
||||
monitor_host: "{{ ansible_host }}"
|
||||
monitor_port: {{ datum_gateway_stratum_port }}
|
||||
monitor_name: "DATUM Stratum (public)"
|
||||
mode: "0644"
|
||||
|
||||
- name: Run Uptime Kuma TCP monitor setup
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
command: python3 /tmp/setup_datum_stratum_tcp_monitor.py
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: tcp_monitor_setup
|
||||
changed_when: "'SUCCESS' in tcp_monitor_setup.stdout"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display TCP monitor setup output
|
||||
debug:
|
||||
msg: "{{ tcp_monitor_setup.stdout_lines }}"
|
||||
when: tcp_monitor_setup.stdout is defined
|
||||
|
||||
- name: Clean up Stratum TCP monitor temporary files
|
||||
when: uptime_kuma_enabled | default(false)
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /tmp/setup_datum_stratum_tcp_monitor.py
|
||||
- /tmp/ansible_datum_stratum_config.yml
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue