deploy-guard: block activation while users are online
- modules/server-deploy-guard.nix: extendable aggregator registered via
services.deployGuard.checks.<name>.{description,command}. Installs
deploy-guard-check with per-check timeout, pass/block reporting, JSON
output, DEPLOY_GUARD_BYPASS / /run/deploy-guard-bypass (single-shot).
- services/jellyfin/jellyfin-deploy-guard.nix: curl+jq on /Sessions,
blocks when any session carries NowPlayingItem; soft-fails when unreachable.
- services/minecraft-deploy-guard.nix: mcstatus SLP query on 25565, blocks
when players.online > 0; soft-fails when unreachable.
- flake.nix: wrap deploy.nodes.muffin activation with activate.custom so
deploy-guard-check runs before switch-to-configuration. Auto-rollback
catches the failure. dryActivate/boot branches preserved.
- deploy.sh: SSH preflight for ./deploy.sh muffin with --force /
DEPLOY_GUARD_FORCE=1 (touches remote bypass marker). Connectivity
failure is soft; activation still enforces.
- tests/deploy-guard.nix: aggregator contract, bypass mechanics, timeout,
JSON output.
This commit is contained in:
67
services/minecraft-deploy-guard.nix
Normal file
67
services/minecraft-deploy-guard.nix
Normal file
@@ -0,0 +1,67 @@
|
||||
# Deploy guard check for the Minecraft server.
|
||||
#
|
||||
# Queries the standard Server List Ping (SLP) handshake on the game port —
|
||||
# no RCON, no query, no extra config. SLP is always enabled and returns the
|
||||
# live player count plus (usually) a short name sample.
|
||||
#
|
||||
# Contract (deploy-guard-check plug-in):
|
||||
# - exit 0: no players online, or the server isn't reachable at all (down ⇒
|
||||
# no users to disrupt).
|
||||
# - exit 1: at least one player is connected; stdout lists the names that
|
||||
# made it into the SLP sample.
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
service_configs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
minecraftPort = service_configs.ports.public.minecraft.port;
|
||||
|
||||
check =
|
||||
pkgs.writers.writePython3Bin "deploy-guard-check-minecraft"
|
||||
{
|
||||
libraries = [ pkgs.python3Packages.mcstatus ];
|
||||
flakeIgnore = [
|
||||
"E501"
|
||||
"E402"
|
||||
];
|
||||
}
|
||||
''
|
||||
import sys
|
||||
|
||||
try:
|
||||
from mcstatus import JavaServer
|
||||
except ImportError as e:
|
||||
print(f"minecraft: mcstatus unavailable ({e}); assuming safe", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
status = JavaServer.lookup("127.0.0.1:${toString minecraftPort}", timeout=5).status()
|
||||
except Exception as e:
|
||||
print(f"minecraft: unreachable ({e}); assuming safe to deploy", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
online = status.players.online
|
||||
if online <= 0:
|
||||
sys.exit(0)
|
||||
|
||||
sample = getattr(status.players, "sample", None) or []
|
||||
names = ", ".join(p.name for p in sample) or "<names not reported>"
|
||||
print(f"Minecraft: {online} player(s) online: {names}")
|
||||
sys.exit(1)
|
||||
'';
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../modules/server-deploy-guard.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf config.services.minecraft-servers.enable {
|
||||
services.deployGuard.checks.minecraft = {
|
||||
description = "Players connected to the Minecraft server";
|
||||
command = check;
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user