Move Pi-hole backup node from Serenity to NOMAD
This commit is contained in:
34
pihole/nomad/bin/prepare_nomad.sh
Executable file
34
pihole/nomad/bin/prepare_nomad.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
STACK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
ENV_FILE="${ENV_FILE:-$STACK_DIR/.env}"
|
||||
DO_UP="${1:-}"
|
||||
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
echo "Missing $ENV_FILE — copy .env.example to .env and fill in real values first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
mkdir -p \
|
||||
"$APPDATA_PIHOLE_ROOT/etc-pihole" \
|
||||
"$APPDATA_PIHOLE_ROOT/etc-dnsmasq.d" \
|
||||
"$APPDATA_UNBOUND_ROOT/var"
|
||||
UNBOUND_TARGET="$APPDATA_UNBOUND_ROOT/unbound.conf"
|
||||
UNBOUND_TMP="$APPDATA_UNBOUND_ROOT/.unbound.conf.tmp.$$"
|
||||
cp "$STACK_DIR/unbound.conf" "$UNBOUND_TMP"
|
||||
mv -f "$UNBOUND_TMP" "$UNBOUND_TARGET"
|
||||
if [[ ! -s "$APPDATA_UNBOUND_ROOT/var/root.hints" ]]; then
|
||||
curl -fsSL -o "$APPDATA_UNBOUND_ROOT/var/root.hints" https://www.internic.net/domain/named.cache
|
||||
fi
|
||||
ENV_FILE="$ENV_FILE" python3 "$STACK_DIR/bin/render_keepalived_conf.py"
|
||||
docker compose --env-file "$ENV_FILE" -f "$STACK_DIR/docker-compose.yaml" config >/dev/null
|
||||
if [[ "$DO_UP" == "--up" ]]; then
|
||||
docker compose --env-file "$ENV_FILE" -f "$STACK_DIR/docker-compose.yaml" up -d
|
||||
docker compose --env-file "$ENV_FILE" -f "$STACK_DIR/docker-compose.yaml" ps
|
||||
fi
|
||||
73
pihole/nomad/bin/render_keepalived_conf.py
Executable file
73
pihole/nomad/bin/render_keepalived_conf.py
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
STACK_DIR = Path(__file__).resolve().parent.parent
|
||||
ENV_FILE = Path(os.environ.get('ENV_FILE', STACK_DIR / '.env'))
|
||||
TEMPLATE = STACK_DIR / 'keepalived.conf.template'
|
||||
OUTPUT = STACK_DIR / 'keepalived.conf'
|
||||
|
||||
|
||||
def load_env(path: Path) -> dict[str, str]:
|
||||
env = dict(os.environ)
|
||||
if path.exists():
|
||||
for raw in path.read_text(encoding='utf-8').splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith('#') or '=' not in line:
|
||||
continue
|
||||
k, v = line.split('=', 1)
|
||||
env.setdefault(k.strip(), v.strip().strip('"').strip("'"))
|
||||
return env
|
||||
|
||||
|
||||
def main() -> int:
|
||||
env = load_env(ENV_FILE)
|
||||
required = [
|
||||
'LAN_INTERFACE', 'VIP_CIDR', 'KEEPALIVED_ROUTER_ID', 'KEEPALIVED_STATE',
|
||||
'KEEPALIVED_PRIORITY', 'KEEPALIVED_PASSWORD'
|
||||
]
|
||||
missing = [k for k in required if not env.get(k)]
|
||||
if missing:
|
||||
print(f'Missing required vars in {ENV_FILE}: {", ".join(missing)}', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
web_port = env.get('PIHOLE_WEB_PORT') or env.get('PD_PIHOLE_WEB_PORT') or env.get('SERENITY_PIHOLE_WEB_PORT') or env.get('RPI4_PIHOLE_WEB_PORT')
|
||||
if not web_port:
|
||||
print('Missing Pi-hole web port in .env (set PIHOLE_WEB_PORT or node-specific port variable).', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
keepalived_password = env['KEEPALIVED_PASSWORD']
|
||||
if len(keepalived_password) > 8:
|
||||
print('KEEPALIVED_PASSWORD must be 8 characters or fewer for VRRP PASS auth.', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
try:
|
||||
web_port_hex = f"{int(web_port):04X}"
|
||||
except ValueError:
|
||||
print('Pi-hole web port must be an integer.', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
router_id_name = f"pihole_{re.sub(r'[^a-z0-9]+', '_', env['KEEPALIVED_STATE'].lower())}"
|
||||
nopreempt_line = ' nopreempt' if env.get('KEEPALIVED_NOPREEMPT', 'true').lower() in {'1','true','yes','on'} else ''
|
||||
|
||||
rendered = TEMPLATE.read_text(encoding='utf-8')
|
||||
replacements = {
|
||||
'ROUTER_ID_NAME': router_id_name,
|
||||
'PIHOLE_WEB_PORT_HEX': web_port_hex,
|
||||
'NOPREEMPT_LINE': nopreempt_line,
|
||||
}
|
||||
replacements.update({k: env[k] for k in required})
|
||||
replacements['PIHOLE_WEB_PORT'] = web_port
|
||||
for key, value in replacements.items():
|
||||
rendered = rendered.replace('${' + key + '}', value)
|
||||
|
||||
OUTPUT.write_text(rendered.rstrip() + '\n', encoding='utf-8')
|
||||
print(f'Rendered {OUTPUT}')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user