Bind daemon to Pacemaker resource identity
This commit is contained in:
@@ -16,6 +16,7 @@ SOCKET_PATH = os.environ.get(
|
|||||||
|
|
||||||
server_socket = None
|
server_socket = None
|
||||||
registry = None
|
registry = None
|
||||||
|
pacemaker_resource_id = None
|
||||||
|
|
||||||
PROTOCOL_VERSION = 1
|
PROTOCOL_VERSION = 1
|
||||||
|
|
||||||
@@ -32,6 +33,38 @@ def protocol_response(payload):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def load_pacemaker_resource_binding(
|
||||||
|
current_registry,
|
||||||
|
environ=None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Load and validate the Pacemaker resource identity accepted by this
|
||||||
|
daemon instance.
|
||||||
|
|
||||||
|
RESOURCE_ID identifies the registry entry. PACEMAKER_RESOURCE_ID is a
|
||||||
|
separate control-plane binding and must resolve to that registry.
|
||||||
|
"""
|
||||||
|
if environ is None:
|
||||||
|
environ = os.environ
|
||||||
|
|
||||||
|
resource_id = environ.get(
|
||||||
|
"PACEMAKER_RESOURCE_ID"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not resource_id:
|
||||||
|
raise RegistryError(
|
||||||
|
"PACEMAKER_RESOURCE_ID is not configured"
|
||||||
|
)
|
||||||
|
|
||||||
|
if resource_id not in current_registry.resources:
|
||||||
|
raise RegistryError(
|
||||||
|
"PACEMAKER_RESOURCE_ID does not identify "
|
||||||
|
f"a registered resource: {resource_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return resource_id
|
||||||
|
|
||||||
|
|
||||||
def handle_request(req):
|
def handle_request(req):
|
||||||
if not isinstance(req, dict):
|
if not isinstance(req, dict):
|
||||||
return protocol_response({
|
return protocol_response({
|
||||||
@@ -65,11 +98,17 @@ def handle_request(req):
|
|||||||
return protocol_response({
|
return protocol_response({
|
||||||
"ok": True,
|
"ok": True,
|
||||||
"daemon": "managed-containerd",
|
"daemon": "managed-containerd",
|
||||||
|
"pacemaker_resource": (
|
||||||
|
pacemaker_resource_id
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
if action == "registry.list":
|
if action == "registry.list":
|
||||||
return protocol_response({
|
return protocol_response({
|
||||||
"ok": True,
|
"ok": True,
|
||||||
|
"pacemaker_resource": (
|
||||||
|
pacemaker_resource_id
|
||||||
|
),
|
||||||
"resources": registry.list_resources(),
|
"resources": registry.list_resources(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -82,6 +121,18 @@ def handle_request(req):
|
|||||||
"error": "resource is required",
|
"error": "resource is required",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if resource != pacemaker_resource_id:
|
||||||
|
return protocol_response({
|
||||||
|
"ok": False,
|
||||||
|
"error_code": "resource_binding_mismatch",
|
||||||
|
"error": (
|
||||||
|
"requested resource does not match "
|
||||||
|
"the configured Pacemaker binding"
|
||||||
|
),
|
||||||
|
"resource": resource,
|
||||||
|
"action": action,
|
||||||
|
})
|
||||||
|
|
||||||
return protocol_response(
|
return protocol_response(
|
||||||
registry.execute(
|
registry.execute(
|
||||||
resource,
|
resource,
|
||||||
@@ -118,8 +169,12 @@ def shutdown(signum, frame):
|
|||||||
def serve():
|
def serve():
|
||||||
global server_socket
|
global server_socket
|
||||||
global registry
|
global registry
|
||||||
|
global pacemaker_resource_id
|
||||||
|
|
||||||
registry = Registry()
|
registry = Registry()
|
||||||
|
pacemaker_resource_id = (
|
||||||
|
load_pacemaker_resource_binding(registry)
|
||||||
|
)
|
||||||
|
|
||||||
os.makedirs(
|
os.makedirs(
|
||||||
os.path.dirname(SOCKET_PATH),
|
os.path.dirname(SOCKET_PATH),
|
||||||
@@ -151,6 +206,12 @@ def serve():
|
|||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Pacemaker resource binding: "
|
||||||
|
+ pacemaker_resource_id,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
conn, _ = server_socket.accept()
|
conn, _ = server_socket.accept()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
RESOURCE_ID=postgresql-ha
|
RESOURCE_ID=postgresql-ha
|
||||||
|
PACEMAKER_RESOURCE_ID=postgresql-ha
|
||||||
PLUGIN=postgresql
|
PLUGIN=postgresql
|
||||||
|
|
||||||
MANAGED_CONTAINERD_SOCKET=/run/managed-containerd/managed-containerd.sock
|
MANAGED_CONTAINERD_SOCKET=/run/managed-containerd/managed-containerd.sock
|
||||||
|
|||||||
Executable
+171
@@ -0,0 +1,171 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, "daemon")
|
||||||
|
|
||||||
|
import server
|
||||||
|
from registry import RegistryError
|
||||||
|
|
||||||
|
|
||||||
|
class FakeRegistry:
|
||||||
|
def __init__(self):
|
||||||
|
self.resources = {
|
||||||
|
"postgresql-ha": object(),
|
||||||
|
}
|
||||||
|
self.executions = []
|
||||||
|
|
||||||
|
def list_resources(self):
|
||||||
|
return {
|
||||||
|
"postgresql-ha": {
|
||||||
|
"resource": "postgresql-ha",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def execute(self, resource, action):
|
||||||
|
self.executions.append(
|
||||||
|
(resource, action)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ok": True,
|
||||||
|
"resource": resource,
|
||||||
|
"plugin": "postgresql",
|
||||||
|
"action": action,
|
||||||
|
"result": {
|
||||||
|
"accepted": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def require(condition, message):
|
||||||
|
if not condition:
|
||||||
|
raise SystemExit("FAIL: " + message)
|
||||||
|
|
||||||
|
|
||||||
|
def require_registry_error(callback, message):
|
||||||
|
try:
|
||||||
|
callback()
|
||||||
|
|
||||||
|
except RegistryError:
|
||||||
|
return
|
||||||
|
|
||||||
|
raise SystemExit("FAIL: " + message)
|
||||||
|
|
||||||
|
|
||||||
|
registry = FakeRegistry()
|
||||||
|
|
||||||
|
print("===== PACEMAKER RESOURCE BINDING =====")
|
||||||
|
|
||||||
|
require_registry_error(
|
||||||
|
lambda: server.load_pacemaker_resource_binding(
|
||||||
|
registry,
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"missing binding was accepted",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("missing_binding rejected")
|
||||||
|
|
||||||
|
require_registry_error(
|
||||||
|
lambda: server.load_pacemaker_resource_binding(
|
||||||
|
registry,
|
||||||
|
{
|
||||||
|
"PACEMAKER_RESOURCE_ID": "other-resource",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
"unknown binding was accepted",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("unknown_binding rejected")
|
||||||
|
|
||||||
|
binding = server.load_pacemaker_resource_binding(
|
||||||
|
registry,
|
||||||
|
{
|
||||||
|
"PACEMAKER_RESOURCE_ID": "postgresql-ha",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require(
|
||||||
|
binding == "postgresql-ha",
|
||||||
|
"valid binding was not loaded",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("valid_binding accepted")
|
||||||
|
|
||||||
|
server.registry = registry
|
||||||
|
server.pacemaker_resource_id = binding
|
||||||
|
|
||||||
|
ping = server.handle_request({
|
||||||
|
"protocol_version": 1,
|
||||||
|
"action": "ping",
|
||||||
|
})
|
||||||
|
|
||||||
|
require(ping["ok"] is True, "ping failed")
|
||||||
|
require(
|
||||||
|
ping["pacemaker_resource"] == "postgresql-ha",
|
||||||
|
"ping did not report the binding",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
ping["protocol_version"] == 1,
|
||||||
|
"ping lost the protocol version",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("ping_binding reported")
|
||||||
|
|
||||||
|
response = server.handle_request({
|
||||||
|
"protocol_version": 1,
|
||||||
|
"resource": "postgresql-ha",
|
||||||
|
"action": "monitor",
|
||||||
|
})
|
||||||
|
|
||||||
|
require(
|
||||||
|
response["ok"] is True,
|
||||||
|
"matching resource was rejected",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
registry.executions
|
||||||
|
== [("postgresql-ha", "monitor")],
|
||||||
|
"matching request was not executed exactly once",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("matching_resource executed")
|
||||||
|
|
||||||
|
response = server.handle_request({
|
||||||
|
"protocol_version": 1,
|
||||||
|
"resource": "different-resource",
|
||||||
|
"action": "monitor",
|
||||||
|
})
|
||||||
|
|
||||||
|
require(
|
||||||
|
response["ok"] is False,
|
||||||
|
"mismatched resource was accepted",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
response["error_code"]
|
||||||
|
== "resource_binding_mismatch",
|
||||||
|
"wrong mismatch classification",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
response["resource"]
|
||||||
|
== "different-resource",
|
||||||
|
"rejected resource was not echoed",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
response["action"] == "monitor",
|
||||||
|
"rejected action was not echoed",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
registry.executions
|
||||||
|
== [("postgresql-ha", "monitor")],
|
||||||
|
"mismatched request reached the registry",
|
||||||
|
)
|
||||||
|
require(
|
||||||
|
response["protocol_version"] == 1,
|
||||||
|
"mismatch response lost protocol metadata",
|
||||||
|
)
|
||||||
|
|
||||||
|
print("mismatched_resource rejected")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("RESOURCE_BINDING_TEST_PASS=1")
|
||||||
Reference in New Issue
Block a user