279 lines
7.7 KiB
Bash
Executable file
279 lines
7.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# black.sh — black-specific provisioning (10.0.0.11, Ubuntu 24.04)
|
|
#
|
|
# Black is the devops + staging host. This module verifies/configures:
|
|
# - dnsmasq (systemd, DNS server)
|
|
# - nginx (docker reverse proxy)
|
|
# - Docker stack (forgejo, verdaccio, runner, restic, serpbear, sso)
|
|
# - PM2 services (landing-api, status-dashboard)
|
|
# - systemd services (marketplace-api, messenger-imessage, host-status-monitor)
|
|
# - PostgreSQL
|
|
# - NFS
|
|
# - Self-DNS (resolved drop-in pointing to 127.0.0.1)
|
|
#
|
|
|
|
BIGDISK="/bigdisk/forgejo"
|
|
COMPOSE_FILE="$BIGDISK/docker-compose.yml"
|
|
|
|
# Expected docker containers from the compose stack
|
|
EXPECTED_CONTAINERS=(
|
|
forgejo
|
|
forgejo-nginx
|
|
forgejo-runner
|
|
verdaccio
|
|
restic-rest-server
|
|
serpbear
|
|
sso-api
|
|
sso-postgres
|
|
sso-redis
|
|
)
|
|
|
|
# Expected PM2 processes
|
|
EXPECTED_PM2=(
|
|
landing-api
|
|
webmap-router
|
|
status-dashboard-api
|
|
status-dashboard-frontend
|
|
)
|
|
|
|
# Expected systemd services
|
|
EXPECTED_SYSTEMD=(
|
|
dnsmasq
|
|
postgresql
|
|
nfs-server
|
|
marketplace-api
|
|
messenger-imessage
|
|
host-status-monitor
|
|
)
|
|
|
|
# ============================================================================
|
|
# Self-DNS (black IS the DNS server)
|
|
# ============================================================================
|
|
|
|
_black_self_dns() {
|
|
log_section "Self-DNS (resolved → 127.0.0.1)"
|
|
|
|
local conf_dir="/etc/systemd/resolved.conf.d"
|
|
local conf_file="$conf_dir/lan.conf"
|
|
|
|
if [[ -f "$conf_file" ]] && grep -q "DNS=127.0.0.1" "$conf_file" 2>/dev/null; then
|
|
log_info "Self-DNS already configured (127.0.0.1)"
|
|
return 0
|
|
fi
|
|
|
|
if [[ "${CHECK_ONLY:-false}" == "true" ]]; then
|
|
log_warn "Self-DNS not configured"
|
|
return 1
|
|
fi
|
|
|
|
log_step "Configuring resolved to use local dnsmasq..."
|
|
sudo mkdir -p "$conf_dir"
|
|
sudo tee "$conf_file" >/dev/null <<EOF
|
|
[Resolve]
|
|
DNS=127.0.0.1
|
|
Domains=~nasty.sh ~black.local ~apricot.local
|
|
EOF
|
|
|
|
sudo systemctl restart systemd-resolved
|
|
log_info "Self-DNS configured"
|
|
}
|
|
|
|
# ============================================================================
|
|
# dnsmasq
|
|
# ============================================================================
|
|
|
|
_black_dnsmasq() {
|
|
log_section "dnsmasq (DNS Server)"
|
|
|
|
local dnsmasq_conf="/etc/dnsmasq.d/lan.conf"
|
|
|
|
if systemctl is-active dnsmasq &>/dev/null; then
|
|
log_info "dnsmasq is active"
|
|
else
|
|
log_error "dnsmasq is NOT active"
|
|
if [[ "${CHECK_ONLY:-false}" != "true" ]]; then
|
|
log_step "Starting dnsmasq..."
|
|
sudo systemctl enable --now dnsmasq
|
|
fi
|
|
fi
|
|
|
|
if [[ -f "$dnsmasq_conf" ]]; then
|
|
log_info "dnsmasq config exists: $dnsmasq_conf"
|
|
else
|
|
log_warn "dnsmasq config missing: $dnsmasq_conf"
|
|
fi
|
|
|
|
# Verify DNS is answering
|
|
if command -v dig &>/dev/null; then
|
|
if dig +short forge.black.local @127.0.0.1 2>/dev/null | grep -q "10.0.0.11"; then
|
|
log_info "dnsmasq resolving forge.black.local correctly"
|
|
else
|
|
log_warn "dnsmasq not resolving forge.black.local"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# nginx (docker)
|
|
# ============================================================================
|
|
|
|
_black_nginx() {
|
|
log_section "nginx (Docker Reverse Proxy)"
|
|
|
|
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "forgejo-nginx"; then
|
|
log_info "nginx container running"
|
|
|
|
# Test config
|
|
local test_result
|
|
test_result=$(docker exec forgejo-nginx nginx -t 2>&1)
|
|
if echo "$test_result" | grep -q "successful"; then
|
|
log_info "nginx config valid"
|
|
else
|
|
log_error "nginx config invalid:"
|
|
echo "$test_result"
|
|
fi
|
|
|
|
# List vhosts
|
|
log_step "Active vhost configs:"
|
|
docker exec forgejo-nginx ls /etc/nginx/conf.d/ 2>/dev/null | while read -r f; do
|
|
echo " $f"
|
|
done
|
|
else
|
|
log_error "nginx container NOT running"
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Docker stack
|
|
# ============================================================================
|
|
|
|
_black_docker_stack() {
|
|
log_section "Docker Stack"
|
|
|
|
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
|
log_error "docker-compose.yml not found: $COMPOSE_FILE"
|
|
return 1
|
|
fi
|
|
|
|
local running
|
|
running=$(docker ps --format '{{.Names}}' 2>/dev/null)
|
|
|
|
for container in "${EXPECTED_CONTAINERS[@]}"; do
|
|
if echo "$running" | grep -q "^${container}$"; then
|
|
log_info "Container: $container"
|
|
else
|
|
log_error "Container NOT running: $container"
|
|
if [[ "${CHECK_ONLY:-false}" != "true" ]]; then
|
|
log_step "Attempting to start docker stack..."
|
|
(cd "$BIGDISK" && docker compose up -d)
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
# ============================================================================
|
|
# PM2 services
|
|
# ============================================================================
|
|
|
|
_black_pm2() {
|
|
log_section "PM2 Services"
|
|
|
|
if ! command -v pm2 &>/dev/null; then
|
|
log_error "pm2 not found"
|
|
return 1
|
|
fi
|
|
|
|
local pm2_list
|
|
pm2_list=$(pm2 jlist 2>/dev/null || echo "[]")
|
|
|
|
for proc in "${EXPECTED_PM2[@]}"; do
|
|
local status
|
|
status=$(echo "$pm2_list" | python3 -c "
|
|
import json, sys
|
|
data = json.load(sys.stdin)
|
|
for p in data:
|
|
if p.get('name') == '$proc':
|
|
print(p.get('pm2_env', {}).get('status', 'unknown'))
|
|
break
|
|
else:
|
|
print('missing')
|
|
" 2>/dev/null || echo "error")
|
|
|
|
case "$status" in
|
|
online) log_info "PM2: $proc (online)" ;;
|
|
stopped) log_warn "PM2: $proc (stopped)" ;;
|
|
missing) log_error "PM2: $proc (not registered)" ;;
|
|
*) log_warn "PM2: $proc ($status)" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ============================================================================
|
|
# systemd services
|
|
# ============================================================================
|
|
|
|
_black_systemd() {
|
|
log_section "Systemd Services"
|
|
|
|
for svc in "${EXPECTED_SYSTEMD[@]}"; do
|
|
if systemctl is-active "$svc" &>/dev/null; then
|
|
log_info "systemd: $svc (active)"
|
|
elif systemctl is-enabled "$svc" &>/dev/null; then
|
|
log_warn "systemd: $svc (enabled but inactive)"
|
|
if [[ "${CHECK_ONLY:-false}" != "true" ]]; then
|
|
log_step "Starting $svc..."
|
|
sudo systemctl start "$svc"
|
|
fi
|
|
else
|
|
log_error "systemd: $svc (not found or disabled)"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# ============================================================================
|
|
# CA cert + NPM (black still needs the CA cert locally)
|
|
# ============================================================================
|
|
|
|
_black_local_config() {
|
|
log_section "Local Configuration"
|
|
|
|
# NPM registry
|
|
if grep -q "registry=http://npm.black.local" ~/.npmrc 2>/dev/null; then
|
|
log_info "NPM registry configured"
|
|
else
|
|
log_warn "NPM registry not configured in ~/.npmrc"
|
|
if [[ "${CHECK_ONLY:-false}" != "true" ]]; then
|
|
_configure_npm_registry
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Public API: called by provision.sh
|
|
# ============================================================================
|
|
|
|
host_dns_setup() {
|
|
_black_self_dns
|
|
}
|
|
|
|
host_services_setup() {
|
|
_black_dnsmasq
|
|
_black_nginx
|
|
_black_docker_stack
|
|
_black_pm2
|
|
_black_systemd
|
|
_black_local_config
|
|
}
|
|
|
|
host_verify() {
|
|
CHECK_ONLY=true
|
|
_black_self_dns
|
|
_black_dnsmasq
|
|
_black_nginx
|
|
_black_docker_stack
|
|
_black_pm2
|
|
_black_systemd
|
|
_black_local_config
|
|
}
|