Bind daemon to local Pacemaker node identity

This commit is contained in:
Lyncoln
2026-09-17 16:03:03 -04:00
parent 338fe2ecac
commit 64cdc024b0
2 changed files with 113 additions and 0 deletions
+43
View File
@@ -20,6 +20,7 @@ registry = None
pacemaker_resource_id = None pacemaker_resource_id = None
pacemaker_peer_uid = None pacemaker_peer_uid = None
pacemaker_peer_gid = None pacemaker_peer_gid = None
pacemaker_node_name = None
PROTOCOL_VERSION = 1 PROTOCOL_VERSION = 1
@@ -68,6 +69,38 @@ def load_pacemaker_resource_binding(
return resource_id return resource_id
def load_pacemaker_node_name(environ=None):
"""
Load the Pacemaker node represented by this daemon instance.
The daemon is local to one PostgreSQL instance. This identity is not
used for routing; it is used to validate promotion and demotion context.
"""
if environ is None:
environ = os.environ
raw_node_name = environ.get("PACEMAKER_NODE_NAME")
if raw_node_name is None:
raise RegistryError(
"PACEMAKER_NODE_NAME is not configured"
)
node_name = raw_node_name.strip().lower()
if not node_name:
raise RegistryError(
"PACEMAKER_NODE_NAME is empty"
)
if any(char.isspace() for char in node_name):
raise RegistryError(
"PACEMAKER_NODE_NAME must not contain whitespace"
)
return node_name
def load_pacemaker_peer_authorization( def load_pacemaker_peer_authorization(
environ=None, environ=None,
): ):
@@ -163,6 +196,7 @@ def handle_request(
"pacemaker_resource": ( "pacemaker_resource": (
pacemaker_resource_id pacemaker_resource_id
), ),
"pacemaker_node": pacemaker_node_name,
}) })
if action == "registry.list": if action == "registry.list":
@@ -171,6 +205,7 @@ def handle_request(
"pacemaker_resource": ( "pacemaker_resource": (
pacemaker_resource_id pacemaker_resource_id
), ),
"pacemaker_node": pacemaker_node_name,
"resources": registry.list_resources(), "resources": registry.list_resources(),
}) })
@@ -263,6 +298,7 @@ def serve():
global pacemaker_resource_id global pacemaker_resource_id
global pacemaker_peer_uid global pacemaker_peer_uid
global pacemaker_peer_gid global pacemaker_peer_gid
global pacemaker_node_name
registry = Registry() registry = Registry()
pacemaker_resource_id = ( pacemaker_resource_id = (
@@ -272,6 +308,7 @@ def serve():
pacemaker_peer_uid, pacemaker_peer_uid,
pacemaker_peer_gid, pacemaker_peer_gid,
) = load_pacemaker_peer_authorization() ) = load_pacemaker_peer_authorization()
pacemaker_node_name = load_pacemaker_node_name()
socket_directory = os.path.dirname( socket_directory = os.path.dirname(
SOCKET_PATH SOCKET_PATH
@@ -321,6 +358,12 @@ def serve():
flush=True, flush=True,
) )
print(
"Pacemaker node identity: "
+ pacemaker_node_name,
flush=True,
)
try: try:
while True: while True:
conn, _ = server_socket.accept() conn, _ = server_socket.accept()
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import sys
sys.path.insert(0, "daemon")
import server
from registry import RegistryError
def require(condition, message):
if not condition:
raise SystemExit("FAIL: " + message)
def require_error(environment, message):
try:
server.load_pacemaker_node_name(environment)
except RegistryError:
return
raise SystemExit("FAIL: " + message)
print("===== PACEMAKER NODE IDENTITY =====")
require_error(
{},
"missing node identity was accepted",
)
print("missing_identity rejected")
require_error(
{"PACEMAKER_NODE_NAME": " "},
"empty node identity was accepted",
)
print("empty_identity rejected")
require_error(
{"PACEMAKER_NODE_NAME": "david prodesk"},
"whitespace node identity was accepted",
)
print("whitespace_identity rejected")
node_name = server.load_pacemaker_node_name({
"PACEMAKER_NODE_NAME": " DAVID-PRODESK ",
})
require(
node_name == "david-prodesk",
"node identity was not normalized",
)
print("valid_identity normalized")
server.pacemaker_node_name = node_name
ping = server.protocol_response({
"ok": True,
"daemon": "managed-containerd",
"pacemaker_node": server.pacemaker_node_name,
})
require(
ping["pacemaker_node"] == "david-prodesk",
"node identity was not reportable",
)
print("reported_identity accepted")
print()
print("NODE_IDENTITY_TEST_PASS=1")