fix(delivery): validate the recovery broker as read-only
This commit is contained in:
@@ -391,7 +391,9 @@ schema-changing upgrades need a separate Backend Alembic procedure.
|
||||
The operator reads the backup through a read-only mount, checks the private receipt
|
||||
and every byte, then copies to fixed targets under 512 MiB, zero swap, 32 tasks,
|
||||
50% CPU and a 30-minute deadline. Original identities/modes and replay IDs are
|
||||
preserved. Old configuration and maintenance receipts go into inactive
|
||||
preserved. Its PostgreSQL target is writable; the fresh RabbitMQ volume is
|
||||
checked through a read-only mount and receives no copied broker state.
|
||||
Old configuration and maintenance receipts go into inactive
|
||||
`recovery/ID/history/`. The new boot's Sandbox preparation owns cgroup recreation.
|
||||
Unknown/nonempty targets and differing existing files are refused; partial restores
|
||||
remain stopped for inspection.
|
||||
|
||||
@@ -21,7 +21,7 @@ from .services import python_environment
|
||||
CONFIGURATION_ROOT = Path("/etc/kf-dependencies")
|
||||
|
||||
|
||||
def storage_observation(name: str) -> dict:
|
||||
def storage_observation(name: str, *, read_only: bool = False) -> dict:
|
||||
"""Read an already provisioned dedicated filesystem, never create/mount one."""
|
||||
directory = Path(STORAGE[name]["path"])
|
||||
preflight.protected(directory.parent, directory=True)
|
||||
@@ -43,18 +43,21 @@ def storage_observation(name: str) -> dict:
|
||||
"shared_elsewhere": any(other[2] == row[2] and other != row for other in mounts),
|
||||
"capacity_bytes": fs.f_frsize * fs.f_blocks,
|
||||
}
|
||||
validate_storage(name, observation)
|
||||
validate_storage(name, observation, read_only=read_only)
|
||||
return observation
|
||||
|
||||
|
||||
def validate_storage(name: str, observation: dict) -> None:
|
||||
def validate_storage(name: str, observation: dict, *, read_only: bool = False) -> None:
|
||||
if type(read_only) is not bool or (read_only and name != "rabbitmq"):
|
||||
raise DeliveryError("unexpected dependency storage observation purpose")
|
||||
required = STORAGE[name]["filesystem_bytes"]
|
||||
if (
|
||||
observation["root"] != "/"
|
||||
or observation["mountpoint"] != STORAGE[name]["path"]
|
||||
or observation["filesystem"] != "ext4"
|
||||
or observation["shared_elsewhere"] is not False
|
||||
or not {"rw", "nosuid", "nodev", "noexec"} <= set(observation["options"])
|
||||
or not {"nosuid", "nodev", "noexec"} <= set(observation["options"])
|
||||
or set(observation["options"]) & {"ro", "rw"} != ({"ro"} if read_only else {"rw"})
|
||||
or type(observation["capacity_bytes"]) is not int
|
||||
or not required * 9 // 10 <= observation["capacity_bytes"] <= required
|
||||
):
|
||||
|
||||
@@ -184,7 +184,8 @@ def restore(recovery_id: str, backup_id: str, expected: str) -> dict:
|
||||
restore_policy.compatible(value, payload, data, root)
|
||||
restore_policy.identity_check(value, selected, root)
|
||||
for name in ("postgres", "rabbitmq"):
|
||||
dependencies.storage_observation(name)
|
||||
# The copy operator writes PostgreSQL only; the fresh broker stays read-only.
|
||||
dependencies.storage_observation(name, read_only=name == "rabbitmq")
|
||||
member(
|
||||
Path(dependencies.STORAGE[name]["path"]),
|
||||
dependency_install.IDENTITIES[name][0],
|
||||
|
||||
Reference in New Issue
Block a user