Reconcile daemon with production runtime state
This commit is contained in:
Executable
+234
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import ast
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.path.insert(0, "daemon")
|
||||
|
||||
from registry import Plugin, Registry, RegistryError, Resource
|
||||
|
||||
|
||||
def require(condition, message):
|
||||
if not condition:
|
||||
raise SystemExit("FAIL: " + message)
|
||||
|
||||
|
||||
def write_plugin(
|
||||
root,
|
||||
name,
|
||||
manifest,
|
||||
source,
|
||||
):
|
||||
plugin_root = Path(root) / name
|
||||
plugin_root.mkdir()
|
||||
|
||||
(plugin_root / "manifest.json").write_text(
|
||||
json.dumps(manifest)
|
||||
)
|
||||
(plugin_root / "plugin.py").write_text(source)
|
||||
|
||||
|
||||
print("===== GENERIC PLUGIN RUNTIME STATE =====")
|
||||
|
||||
with tempfile.TemporaryDirectory() as temporary_root:
|
||||
stateful_manifest = {
|
||||
"name": "stateful",
|
||||
"version": 1,
|
||||
"module": "plugin.py",
|
||||
"actions": ["monitor"],
|
||||
"required_config": ["REQUIRED_VALUE"],
|
||||
"optional_config": ["OPTIONAL_VALUE"],
|
||||
}
|
||||
|
||||
write_plugin(
|
||||
temporary_root,
|
||||
"stateful",
|
||||
stateful_manifest,
|
||||
"""
|
||||
def monitor(config, runtime_state):
|
||||
counter = runtime_state.get("counter", 0) + 1
|
||||
runtime_state["counter"] = counter
|
||||
|
||||
return {
|
||||
"counter": counter,
|
||||
"required": config["REQUIRED_VALUE"],
|
||||
"optional": config["OPTIONAL_VALUE"],
|
||||
}
|
||||
""",
|
||||
)
|
||||
|
||||
environment = {
|
||||
"MANAGED_CONTAINERD_PLUGIN_ROOT": (
|
||||
temporary_root
|
||||
),
|
||||
"RESOURCE_ID": "stateful-resource",
|
||||
"PLUGIN": "stateful",
|
||||
"REQUIRED_VALUE": "required",
|
||||
"OPTIONAL_VALUE": "optional",
|
||||
}
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
environment,
|
||||
clear=False,
|
||||
):
|
||||
registry = Registry()
|
||||
|
||||
first = registry.execute(
|
||||
"stateful-resource",
|
||||
"monitor",
|
||||
)
|
||||
second = registry.execute(
|
||||
"stateful-resource",
|
||||
"monitor",
|
||||
)
|
||||
|
||||
require(
|
||||
first["result"]["counter"] == 1,
|
||||
"first stateful invocation has wrong counter",
|
||||
)
|
||||
require(
|
||||
second["result"]["counter"] == 2,
|
||||
"runtime state did not persist",
|
||||
)
|
||||
require(
|
||||
second["result"]["required"] == "required",
|
||||
"required configuration was not injected",
|
||||
)
|
||||
require(
|
||||
second["result"]["optional"] == "optional",
|
||||
"optional configuration was not injected",
|
||||
)
|
||||
|
||||
resource = registry.resources[
|
||||
"stateful-resource"
|
||||
]
|
||||
|
||||
require(
|
||||
resource.runtime_state == {"counter": 2},
|
||||
"resource runtime state has unexpected contents",
|
||||
)
|
||||
|
||||
print("stateful_handler persisted")
|
||||
print("optional_config injected")
|
||||
|
||||
legacy_manifest = {
|
||||
"name": "legacy",
|
||||
"version": 1,
|
||||
"module": "plugin.py",
|
||||
"actions": ["monitor"],
|
||||
"required_config": ["REQUIRED_VALUE"],
|
||||
}
|
||||
|
||||
write_plugin(
|
||||
temporary_root,
|
||||
"legacy",
|
||||
legacy_manifest,
|
||||
"""
|
||||
def monitor(config):
|
||||
return {
|
||||
"legacy": True,
|
||||
"required": config["REQUIRED_VALUE"],
|
||||
}
|
||||
""",
|
||||
)
|
||||
|
||||
legacy_plugin = Plugin(
|
||||
temporary_root,
|
||||
"legacy",
|
||||
)
|
||||
|
||||
legacy_result = legacy_plugin.execute(
|
||||
"monitor",
|
||||
{"REQUIRED_VALUE": "required"},
|
||||
{},
|
||||
)
|
||||
|
||||
require(
|
||||
legacy_result == {
|
||||
"legacy": True,
|
||||
"required": "required",
|
||||
},
|
||||
"one-argument plugin compatibility failed",
|
||||
)
|
||||
|
||||
print("legacy_handler compatible")
|
||||
|
||||
try:
|
||||
Resource(
|
||||
"invalid-resource",
|
||||
legacy_plugin,
|
||||
{"REQUIRED_VALUE": None},
|
||||
)
|
||||
except RegistryError:
|
||||
print("required_config enforced")
|
||||
else:
|
||||
raise SystemExit(
|
||||
"FAIL: missing required configuration accepted"
|
||||
)
|
||||
|
||||
production_manifest = json.loads(
|
||||
Path(
|
||||
"plugins/postgresql/manifest.json"
|
||||
).read_text()
|
||||
)
|
||||
|
||||
require(
|
||||
production_manifest.get("version") == 3,
|
||||
"PostgreSQL manifest version is not 3",
|
||||
)
|
||||
require(
|
||||
"PG_REPLICATION_PEERS"
|
||||
in production_manifest.get(
|
||||
"required_config",
|
||||
[],
|
||||
),
|
||||
"replication peers are not required",
|
||||
)
|
||||
require(
|
||||
"PG_RECONNECT_GRACE_SECONDS"
|
||||
in production_manifest.get(
|
||||
"optional_config",
|
||||
[],
|
||||
),
|
||||
"reconnect grace is not optional",
|
||||
)
|
||||
|
||||
plugin_tree = ast.parse(
|
||||
Path(
|
||||
"plugins/postgresql/plugin.py"
|
||||
).read_text()
|
||||
)
|
||||
|
||||
monitor_functions = [
|
||||
node
|
||||
for node in plugin_tree.body
|
||||
if isinstance(node, ast.FunctionDef)
|
||||
and node.name == "monitor"
|
||||
]
|
||||
|
||||
require(
|
||||
len(monitor_functions) == 1,
|
||||
"PostgreSQL monitor entry point is ambiguous",
|
||||
)
|
||||
|
||||
monitor_arguments = [
|
||||
argument.arg
|
||||
for argument
|
||||
in monitor_functions[0].args.args
|
||||
]
|
||||
|
||||
require(
|
||||
monitor_arguments[:2]
|
||||
== ["config", "runtime_state"],
|
||||
"PostgreSQL monitor does not accept runtime state",
|
||||
)
|
||||
|
||||
print("postgresql_contract validated")
|
||||
print()
|
||||
print("RUNTIME_STATE_TEST_PASS=1")
|
||||
Reference in New Issue
Block a user