Publish curated Unraid-AppOps-Gateway source from ba66029223865607deddc234e283c11127324eb8

This commit is contained in:
Public Source Publisher committed 2026-10-07 01:56:22 +00:00
1 parent 820244dbb3
commit 8dd4b40252
52 files changed
+11814 -288

No files matched your search

@@ -24,6 +24,29 @@ spec.loader.exec_module(module)
REVISION = "a" * 40
IMAGE_ID = "sha256:" + "b" * 64
ORIGINAL_COMPOSE = b"name: gitea-deploy-runners\nservices:\n deploy-control:\n image: immutable-controller\n"
# Frozen historical layouts; these fixtures must not follow the acceptance registry.
HISTORICAL_PRE_CANDIDATE_RELEASE_FILES = (
"controller_paths.py", "mcp_hub_release_store.py", "deploy.py",
"managed_appdata_paths.py", "prepare_managed_appdata_root.py", "runner_fleet.py",
"runner_capacity_controller.py", "runner_capacity_policy.py",
"runner_fleet_upgrade_v2.py", "status_server.py",
"repopulse_adoption_store.py", "repopulse_registry_restore.py",
"repopulse_adoption_executor.py", "repopulse_adoption_effects.py",
"repopulse_adoption_storage_effects.py", "repopulse_target_attestation.py",
"stage_repopulse_target_attestation.py", "repopulse_adoption_cli.py",
"stage_repopulse_maintenance_environment.py", "run_repopulse_adoption.py",
)
HISTORICAL_CANDIDATE_HELPER_RELEASE_FILES = (
"controller_paths.py", "mcp_hub_release_store.py", "deploy.py",
"managed_appdata_paths.py", "prepare_managed_appdata_root.py", "runner_fleet.py",
"runner_capacity_controller.py", "runner_capacity_policy.py",
"runner_fleet_upgrade_v2.py", "status_server.py", "prepare_mcp_hub_candidate.py",
"repopulse_adoption_store.py", "repopulse_registry_restore.py",
"repopulse_adoption_executor.py", "repopulse_adoption_effects.py",
"repopulse_adoption_storage_effects.py", "repopulse_target_attestation.py",
"stage_repopulse_target_attestation.py", "repopulse_adoption_cli.py",
"stage_repopulse_maintenance_environment.py", "run_repopulse_adoption.py",
)
HISTORICAL_CONFIG_ROOT_RELEASE_FILES = (
"controller_paths.py", "mcp_hub_release_store.py", "hub_permission_preparation.py",
"deploy.py", "managed_appdata_paths.py", "prepare_managed_appdata_root.py", "runner_fleet.py",
@@ -279,6 +302,220 @@ def test_promotion_does_not_prepare_backup_volume_when_staged_configuration_fail
assert "backup_volume_prepared" not in events
@pytest.mark.parametrize(
("failed_step", "expected_phase", "error_code"),
[
("old_tls", "old_tls", "controller_volume_private_tls_unsafe"),
("config", "config", "controller_volume_config_missing"),
("release_build", "release_build", "controller_volume_source_archive_incomplete"),
("staged_config", "staged_config", "controller_volume_command_failed"),
("old_release", "old_release", "controller_volume_writer_failed"),
("new_release", "new_release", "controller_volume_writer_failed"),
],
)
def test_prepare_reports_only_fixed_failure_phase_and_code(
monkeypatch, tmp_path, capsys, failed_step, expected_phase, error_code
):
_prepare_promotion_doubles(monkeypatch, tmp_path)
def fail(*_args, **_kwargs):
raise module.VolumeTransitionError(error_code)
if failed_step == "old_tls":
monkeypatch.setattr(module, "_validate_previous_private_tls", fail)
elif failed_step == "config":
monkeypatch.setattr(module, "_read_config_leaf", fail)
elif failed_step == "release_build":
monkeypatch.setattr(module, "_prepare_merged_tree", fail)
elif failed_step == "staged_config":
monkeypatch.setattr(module, "_validate_staged_configuration", fail)
else:
installed = 0
def install(*_args, **_kwargs):
nonlocal installed
installed += 1
if installed == (1 if failed_step == "old_release" else 2):
fail()
monkeypatch.setattr(module, "_install_release_leaf", install)
monkeypatch.setattr(sys, "argv", [
"preparer", "prepare", "--stage-root", str(tmp_path / "stage"),
"--source-revision", "d" * 40, "--source-tree", "e" * 40,
"--controller-version", "2.2.0", "--image-id", IMAGE_ID,
])
with pytest.raises(SystemExit) as failure:
module.main()
assert str(failure.value) == (
"controller protected Hub volume transition failed "
f"phase={expected_phase} code={error_code}"
)
assert capsys.readouterr().out == ""
@pytest.mark.parametrize("error", [
module.VolumeTransitionError("controller_volume_writer_failed\nTOKEN=private"),
module.VolumeTransitionError("controller_volume_unreviewed_code"),
ValueError("TOKEN=private"),
])
def test_prepare_diagnostic_suppresses_untrusted_error_and_phase(monkeypatch, tmp_path, error):
def forged_prepare(*_args, **kwargs):
kwargs["on_phase"]("old_release\nTOKEN=private")
raise error
monkeypatch.setattr(module, "prepare_promotion", forged_prepare)
monkeypatch.setattr(sys, "argv", [
"preparer", "prepare", "--stage-root", str(tmp_path / "stage"),
"--source-revision", "d" * 40, "--source-tree", "e" * 40,
"--controller-version", "2.2.0", "--image-id", IMAGE_ID,
])
with pytest.raises(SystemExit) as failure:
module.main()
assert str(failure.value) == (
"controller protected Hub volume transition failed phase=prepare code=unclassified"
)
@pytest.mark.parametrize(
("stderr", "expected_condition"),
[
(b"container rootfs is marked read-only at /private/path TOKEN=fixture-secret",
"rootfs_read_only"),
(b"read-only file system at /private/path TOKEN=fixture-secret", "mount_read_only"),
(b"permission denied: /private/path TOKEN=fixture-secret", "permission_denied"),
(b"no such file or directory: /private/path TOKEN=fixture-secret",
"destination_missing"),
(b"No such container: private-container-id TOKEN=fixture-secret", "container_missing"),
(b"client version is too old TOKEN=fixture-secret", "api_version_unsupported"),
(b"unrecognized failure TOKEN=fixture-secret", None),
],
)
def test_writer_command_failure_is_categorized_without_retaining_stderr(
monkeypatch, stderr, expected_condition
):
monkeypatch.setattr(module.subprocess, "run", lambda *_args, **_kwargs: types.SimpleNamespace(
returncode=1, stdout=b"", stderr=stderr,
))
with pytest.raises(module.VolumeTransitionError) as failure:
module._run(
["docker", "cp", "-", "container:/private/path"],
data=b"TOKEN=fixture-secret",
diagnostic_action="writer_copy",
)
assert failure.value.args == ("controller_volume_command_failed",)
assert failure.value.command_failure == (
"writer_copy", "exit_1", expected_condition,
)
diagnostic = module._safe_command_diagnostic(failure.value.command_failure)
assert diagnostic is not None
assert "fixture-secret" not in repr(failure.value)
assert "fixture-secret" not in diagnostic
assert "private/path" not in diagnostic
@pytest.mark.parametrize(
("failure_kind", "expected_outcome"),
[("timeout", "timeout"), ("spawn", "spawn_failed"), ("overflow", "output_limit")],
)
def test_writer_command_failure_diagnostics_bound_non_exit_outcomes(
monkeypatch, failure_kind, expected_outcome
):
def run(*_args, **_kwargs):
if failure_kind == "timeout":
raise subprocess.TimeoutExpired(cmd=["docker", "cp"], timeout=45,
stderr=b"TOKEN=fixture-secret")
if failure_kind == "spawn":
raise OSError("TOKEN=fixture-secret")
return types.SimpleNamespace(returncode=0, stdout=b"oversized", stderr=b"")
monkeypatch.setattr(module.subprocess, "run", run)
kwargs = {"max_output": 1} if failure_kind == "overflow" else {}
with pytest.raises(module.VolumeTransitionError) as failure:
module._run(["docker", "cp", "-", "container:/target"],
diagnostic_action="writer_copy", **kwargs)
assert failure.value.command_failure == ("writer_copy", expected_outcome, None)
def test_prepare_reports_only_allowlisted_command_and_stderr_conditions(monkeypatch, tmp_path):
def failed_prepare(*_args, **kwargs):
kwargs["on_phase"]("old_release")
raise module.VolumeTransitionError(
"controller_volume_command_failed",
command_failure=("writer_copy", "exit_1", "rootfs_read_only"),
cleanup_failure=("writer_cleanup", "exit_2", "permission_denied"),
)
monkeypatch.setattr(module, "prepare_promotion", failed_prepare)
monkeypatch.setattr(sys, "argv", [
"preparer", "prepare", "--stage-root", str(tmp_path / "stage"),
"--source-revision", "d" * 40, "--source-tree", "e" * 40,
"--controller-version", "2.2.0", "--image-id", IMAGE_ID,
])
with pytest.raises(SystemExit) as failure:
module.main()
assert str(failure.value) == (
"controller protected Hub volume transition failed phase=old_release "
"code=controller_volume_command_failed "
"diagnostic=writer_copy_exit_1_rootfs_read_only "
"cleanupDiagnostic=writer_cleanup_exit_2_permission_denied"
)
@pytest.mark.parametrize("value", [
("docker cp /private/file", "exit_1", None),
("writer_copy", "exit_1", "TOKEN=fixture-secret"),
("writer_copy", "exit_999", None),
("writer_copy", "exit_1", "unknown_condition"),
["writer_copy", "exit_1", None],
])
def test_command_diagnostic_rejects_unreviewed_fields(value):
assert module._safe_command_diagnostic(value) is None
def test_release_writer_cleanup_failure_preserves_primary_copy_failure(monkeypatch):
partial, expected = _release_with_config()
monkeypatch.setattr(module, "_read_release_leaf", lambda _revision: partial)
monkeypatch.setattr(module, "ensure_volume", lambda **_kwargs: None)
monkeypatch.setattr(module, "_remove_stale_writer", lambda _image_id: None)
monkeypatch.setattr(module, "_inspect_writer", lambda *_args, **_kwargs: {})
calls = []
writer_id = "f" * 64
def run(arguments, *, diagnostic_action=None, **_kwargs):
calls.append(diagnostic_action)
if diagnostic_action == "writer_create":
return writer_id.encode()
if diagnostic_action == "writer_wait":
return b"0"
if diagnostic_action == "writer_copy":
raise module.VolumeTransitionError(
"controller_volume_command_failed",
command_failure=("writer_copy", "exit_1", "rootfs_read_only"),
)
if diagnostic_action == "writer_cleanup":
raise module.VolumeTransitionError(
"controller_volume_command_failed",
command_failure=("writer_cleanup", "exit_2", "permission_denied"),
)
return b""
monkeypatch.setattr(module, "_run", run)
with pytest.raises(module.VolumeTransitionError) as failure:
module._install_release_leaf(REVISION, expected, IMAGE_ID)
assert failure.value.command_failure == (
"writer_copy", "exit_1", "rootfs_read_only",
)
assert failure.value.cleanup_failure == (
"writer_cleanup", "exit_2", "permission_denied",
)
assert calls[-2:] == ["writer_copy", "writer_cleanup"]
def writer_inventory(inherited_labels):
identifier = "c" * 64
spec = module._writer_spec(IMAGE_ID, REVISION, kind="config")
@@ -542,6 +779,90 @@ def test_compose_accepts_empty_bind_normalization_and_canonicalizes_both_managed
}
def test_compose_normalizes_all_observed_live_generated_bind_defaults(compose):
baseline, _calls = compose
volumes = baseline["services"][module.SERVICE]["volumes"]
for mount in volumes:
mount["bind"] = {"create_host_path": True}
rendered = json.loads(module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID))
graph = next(mount for mount in rendered["services"][module.SERVICE]["volumes"]
if mount["target"] == module.DOCKER_ROOT.as_posix())
assert graph == {
"type": "bind", "source": module.DOCKER_ROOT.as_posix(),
"target": module.DOCKER_ROOT.as_posix(), "read_only": True,
}
module._bound_model(json.dumps(rendered).encode())
@pytest.mark.parametrize("bind", [
{"create_host_path": 1}, {"create_host_path": "true"},
{"create_host_path": False},
{"create_host_path": True, "propagation": "rslave"},
{"create_host_path": True, "unknown": True},
])
def test_compose_rejects_unsafe_generated_docker_root_bind(compose, bind):
baseline, _calls = compose
graph = next(mount for mount in baseline["services"][module.SERVICE]["volumes"]
if mount["target"] == module.DOCKER_ROOT.as_posix())
graph["bind"] = bind
with pytest.raises(module.VolumeTransitionError):
module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID)
@pytest.mark.parametrize("target", [module.EXECUTABLE_TARGET, module.CONFIG_TARGET])
@pytest.mark.parametrize("private", [False, True])
def test_compose_normalizes_generated_create_host_path_for_fixed_readonly_mounts(
compose, target, private
):
baseline, _calls = compose
volumes = baseline["services"][module.SERVICE]["volumes"]
bind = {"create_host_path": True}
if private:
bind["propagation"] = "rprivate"
if target == module.EXECUTABLE_TARGET:
mount = next(item for item in volumes if item["target"] == target)
mount["bind"] = bind
else:
volumes.append({
"type": "bind", "source": module._expected_config_path().as_posix(),
"target": target, "read_only": True, "bind": bind,
})
rendered = json.loads(module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID))
managed = [mount for mount in rendered["services"][module.SERVICE]["volumes"]
if mount["target"] in {module.EXECUTABLE_TARGET, module.CONFIG_TARGET}]
assert len(managed) == 2
assert all(mount["read_only"] is True for mount in managed)
assert all(mount["bind"] == {"propagation": "rprivate"} for mount in managed)
assert rendered["services"]["runner"] == baseline["services"]["runner"]
# Only captured source metadata is normalized. Activation stays canonical.
module._bound_model(json.dumps(rendered).encode())
next(mount for mount in managed if mount["target"] == target)["bind"]["create_host_path"] = True
with pytest.raises(module.VolumeTransitionError):
module._bound_model(json.dumps(rendered).encode())
@pytest.mark.parametrize("target", [module.EXECUTABLE_TARGET, module.CONFIG_TARGET])
@pytest.mark.parametrize("bind", [
{"create_host_path": 1},
{"create_host_path": "true"},
{"create_host_path": True, "propagation": "rslave"},
{"create_host_path": True, "selinux": "z"},
])
def test_compose_rejects_unsafe_or_untyped_generated_bind_metadata(compose, target, bind):
baseline, _calls = compose
volumes = baseline["services"][module.SERVICE]["volumes"]
if target == module.EXECUTABLE_TARGET:
next(mount for mount in volumes if mount["target"] == target)["bind"] = bind
else:
volumes.append({
"type": "bind", "source": module._expected_config_path().as_posix(),
"target": target, "read_only": True, "bind": bind,
})
with pytest.raises(module.VolumeTransitionError):
module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID)
@pytest.mark.parametrize(
("target", "bind"),
[
@@ -869,6 +1190,117 @@ def _forge_secret_entries():
}
def _release_with_config(*, forge_secrets=False):
release = {
name: (f'fixed:{name}\n'.encode(), 0o644)
for name in module.RELEASE_FILES
}
manifest = {
'apiVersion': 'itworx.controller-artifact/v1',
'controllerVersion': '2.1.14',
'sourceRepository': 'jens/unraid-appops-gateway',
'sourceRevision': REVISION,
'sourceTree': 'c' * 40,
'artifactSha256': module._artifact_digest(release),
'builtAt': '2026-10-04T00:00:00Z',
}
release['controller-release.json'] = (json.dumps(manifest).encode(), 0o644)
config = {'broker-token': (b'fixture-token', 0o600)}
directories = {'': 0o755}
if forge_secrets:
config.update(_forge_secret_entries())
directories[module.FORGE_SECRET_DIRECTORY] = 0o700
return (release, {'': 0o755}), (release | config, directories)
def _fake_release_writer(monkeypatch, state):
writer_id = 'f' * 64
calls = []
def run(arguments, *, data=None, **_kwargs):
calls.append(arguments)
if arguments[:2] == ['docker', 'create']:
return writer_id.encode()
if arguments[:2] == ['docker', 'wait']:
return b'0'
if arguments[:2] == ['docker', 'cp']:
state['tree'] = module._read_tar_tree(data)
with tarfile.open(fileobj=io.BytesIO(data), mode='r:') as archive:
archive_mode = '-a' in arguments
state['owners'] = {
member.name: ((0, 0) if archive_mode else (member.uid, member.gid))
for member in archive.getmembers() if member.isfile()
}
return b''
monkeypatch.setattr(module, 'ensure_volume', lambda **_kwargs: None)
monkeypatch.setattr(module, '_remove_stale_writer', lambda _image_id: None)
monkeypatch.setattr(module, '_inspect_writer', lambda *_args, **_kwargs: {})
monkeypatch.setattr(module, '_run', run)
return calls
def test_existing_exact_release_subset_is_augmented_and_read_back(monkeypatch):
old, expected = _release_with_config()
state = {'tree': old}
monkeypatch.setattr(module, '_read_release_leaf', lambda _revision: state['tree'])
calls = _fake_release_writer(monkeypatch, state)
module._install_release_leaf(REVISION, expected, IMAGE_ID)
assert state['tree'] == expected
assert len([call for call in calls if call[:2] == ['docker', 'create']]) == 1
assert len([call for call in calls if call[:2] == ['docker', 'cp']]) == 1
def test_divergent_existing_release_refuses_before_writer(monkeypatch):
old, expected = _release_with_config()
damaged = dict(old[0])
damaged['status_server.py'] = (b'tampered', 0o644)
monkeypatch.setattr(module, '_read_release_leaf', lambda _revision: (damaged, old[1]))
calls = _fake_release_writer(monkeypatch, {'tree': None})
with pytest.raises(module.VolumeTransitionError, match='release_collision'):
module._install_release_leaf(REVISION, expected, IMAGE_ID)
assert not any(call[:2] == ['docker', 'create'] for call in calls)
def test_complete_existing_release_uses_no_writer(monkeypatch):
_old, expected = _release_with_config()
monkeypatch.setattr(module, '_read_release_leaf', lambda _revision: expected)
calls = _fake_release_writer(monkeypatch, {'tree': None})
module._install_release_leaf(REVISION, expected, IMAGE_ID)
assert not any(call[:2] == ['docker', 'create'] for call in calls)
def test_release_copy_preserves_fixed_forge_file_owners_on_readback(monkeypatch):
_old, expected = _release_with_config(forge_secrets=True)
state = {'tree': None, 'owners': {}}
def read_leaf(_revision):
if state['tree'] is not None:
for name, owner in module.FORGE_SECRET_OWNERS.items():
if state['owners'].get(name) != owner:
raise module.VolumeTransitionError('controller_volume_tree_file_unsafe')
return state['tree']
monkeypatch.setattr(module, '_read_release_leaf', read_leaf)
calls = _fake_release_writer(monkeypatch, state)
module._install_release_leaf(REVISION, expected, IMAGE_ID)
assert state['tree'] == expected
assert all(state['owners'][name] == owner
for name, owner in module.FORGE_SECRET_OWNERS.items())
assert len([call for call in calls if call[:2] == ['docker', 'cp']]) == 1
assert next(call for call in calls if call[:2] == ['docker', 'cp']) == [
'docker', 'cp', '-',
f"{'f' * 64}:{module.TARGET}/{module.ARTIFACTS}/{REVISION}",
]
def test_complete_forge_secret_bundle_has_exact_export_modes_and_owners():
entries = {
'projects.json': (b'{}', 0o600),
@@ -931,7 +1363,7 @@ def test_forge_secret_tar_owner_drift_is_rejected():
module._read_tar_tree(output.getvalue())
def test_config_writer_uses_archive_mode_to_preserve_forge_leaf_owners(monkeypatch):
def test_config_writer_uses_tar_ownership_without_copy_archive_mode(monkeypatch):
tree = (
{
'projects.json': (b'{}', 0o600),
@@ -942,8 +1374,10 @@ def test_config_writer_uses_archive_mode_to_preserve_forge_leaf_owners(monkeypat
{'': 0o755, module.FORGE_SECRET_DIRECTORY: 0o700},
)
writer_id = 'f' * 64
expected_archive = module._release_tar_bytes('a' * 40, tree)
commands = []
copy_payloads = []
copy_owners = {}
def run(arguments, *, data=None, **_kwargs):
commands.append(arguments)
@@ -953,11 +1387,17 @@ def test_config_writer_uses_archive_mode_to_preserve_forge_leaf_owners(monkeypat
return b'0'
if arguments[:2] == ['docker', 'cp']:
copy_payloads.append(data)
with tarfile.open(fileobj=io.BytesIO(data), mode='r:') as archive:
archive_mode = '-a' in arguments
copy_owners.update({
member.name: ((0, 0) if archive_mode else (member.uid, member.gid))
for member in archive.getmembers() if member.isfile()
})
return b''
monkeypatch.setattr(module, '_read_partial_config_leaf', lambda: None)
monkeypatch.setattr(module, 'ensure_volume', lambda **_kwargs: None)
monkeypatch.setattr(module, '_release_tar_bytes', lambda *_args, **_kwargs: b'archive')
monkeypatch.setattr(module, '_release_tar_bytes', lambda *_args, **_kwargs: expected_archive)
monkeypatch.setattr(module, '_remove_stale_writer', lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, '_writer_spec', lambda *_args, **_kwargs: ['docker', 'create'])
monkeypatch.setattr(module, '_inspect_writer', lambda *_args, **_kwargs: {})
@@ -967,9 +1407,11 @@ def test_config_writer_uses_archive_mode_to_preserve_forge_leaf_owners(monkeypat
module._install_config_tree(tree, IMAGE_ID)
assert [command for command in commands if command[:2] == ['docker', 'cp']] == [[
'docker', 'cp', '-a', '-', f'{writer_id}:{module.TARGET}/{module.CONFIG_DIR}',
'docker', 'cp', '-', f'{writer_id}:{module.TARGET}/{module.CONFIG_DIR}',
]]
assert copy_payloads == [b'archive']
assert copy_payloads == [expected_archive]
assert all(copy_owners[name] == owner
for name, owner in module.FORGE_SECRET_OWNERS.items())
def test_partial_forge_bundle_fails_during_promotion_preparation_before_stop(
@@ -1215,6 +1657,7 @@ def test_writer_is_exact_image_only_bounded_and_has_no_host_paths(kind):
assert module.WRITER_NAME in command
assert IMAGE_ID in command
assert "--network" in command and command[command.index("--network") + 1] == "none"
assert command[command.index("--user") + 1] == "0:0"
assert "--read-only" in command and "--cap-drop" in command
assert "--cpu-quota" in command and "--memory" in command and "--pids-limit" in command
assert "--mount" in command
@@ -1223,6 +1666,21 @@ def test_writer_is_exact_image_only_bounded_and_has_no_host_paths(kind):
assert not any(item.startswith(("/mnt/", "/var/lib/")) for item in command)
@pytest.mark.parametrize("action", ["writer_copy", "config_writer_copy"])
def test_copy_diagnostic_classifies_missing_passwd_user_without_retaining_user_value(action):
stderr = (b'Error response from daemon: getent unable to find entry '
b'"fixture-user-name" in passwd database')
assert module._command_stderr_condition(stderr, action, max_bytes=1024) == (
"passwd_user_lookup_failed"
)
diagnostic = module._safe_command_diagnostic(
(action, "exit_1", "passwd_user_lookup_failed")
)
assert diagnostic == f"{action}_exit_1_passwd_user_lookup_failed"
assert "fixture-user-name" not in diagnostic
def test_promoted_compose_actions_use_only_stdin_and_fixed_service(compose):
baseline, calls = compose
rendered = module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID)
@@ -1307,9 +1765,44 @@ def test_installer_release_order_matches_renderer_contract():
renderer = importlib.util.module_from_spec(renderer_spec)
renderer_spec.loader.exec_module(renderer)
assert tuple(module.RELEASE_FILES) == tuple(renderer._ARTIFACT_FILES)
assert len(renderer._ARTIFACT_FILES) == len(set(renderer._ARTIFACT_FILES))
assert module.RELEASE_FILES[:3] == (
"controller_paths.py", "mcp_hub_release_store.py", "hub_permission_preparation.py"
)
candidate_helper = "prepare_mcp_hub_candidate.py"
assert candidate_helper in module.RELEASE_FILES
assert candidate_helper in renderer._ARTIFACT_FILES
assert module.RELEASE_FILES.count(candidate_helper) == 1
previous_files = module.LEGACY_RELEASE_FILES_PRE_CANDIDATE_HELPER
assert len(previous_files) == 20
previous_with_helper = module.LEGACY_RELEASE_FILES_WITH_CANDIDATE_HELPER
assert len(previous_with_helper) == 21
assert previous_with_helper == tuple(
[*previous_files[:previous_files.index("status_server.py") + 1], candidate_helper,
*previous_files[previous_files.index("status_server.py") + 1:]]
)
assert module.LEGACY_RELEASE_FILES_WITH_CONFIG_ROOT == HISTORICAL_CONFIG_ROOT_RELEASE_FILES
assert module._KNOWN_RELEASE_FILE_SETS == (
module.RELEASE_FILES,
module.LEGACY_RELEASE_FILES_PRE_COMPONENT,
module.LEGACY_RELEASE_FILES_WITH_ROLE_HELPER,
module.LEGACY_RELEASE_FILES_WITHOUT_ROLE_HELPER,
module.LEGACY_RELEASE_FILES_BEFORE_FORGE,
previous_files,
previous_with_helper,
HISTORICAL_CONFIG_ROOT_RELEASE_FILES,
module.LEGACY_RELEASE_FILES_2_1_13,
module.LEGACY_RELEASE_FILES_2_1_5,
)
assert all(
candidate_helper not in legacy
for legacy in (
previous_files,
module.LEGACY_RELEASE_FILES_WITH_CONFIG_ROOT,
module.LEGACY_RELEASE_FILES_2_1_13,
module.LEGACY_RELEASE_FILES_2_1_5,
)
)
assert module.RELEASE_FILES[module.RELEASE_FILES.index("forge_first_install.py") + 1] == (
"forge_database_credentials.py"
)
@@ -1319,7 +1812,7 @@ def test_installer_release_order_matches_renderer_contract():
)
assert module.LEGACY_RELEASE_FILES_WITH_ROLE_HELPER == tuple(
name for name in module.RELEASE_FILES
if name not in {"forge_backup_paths.py", "forge_secret_bundle.py"}
if name not in {"forge_backup_paths.py", "forge_secret_bundle.py", "immutable_service_promotion.py", "registered_legacy_adoption.py", "registered_legacy_transport.py", "registered_build_context.py"}
)
assert module.LEGACY_RELEASE_FILES_WITHOUT_ROLE_HELPER == tuple(
name for name in module.LEGACY_RELEASE_FILES_WITH_ROLE_HELPER
@@ -1339,6 +1832,106 @@ def test_installer_release_order_matches_renderer_contract():
]
def test_pre_candidate_20_file_manifest_remains_accepted_and_current_digest_requires_helper():
files = HISTORICAL_PRE_CANDIDATE_RELEASE_FILES
assert module.LEGACY_RELEASE_FILES_PRE_CANDIDATE_HELPER == files
entries = {name: (f"pre-candidate:{name}".encode(), 0o644) for name in files}
manifest = {
"apiVersion": "itworx.controller-artifact/v1",
"controllerVersion": "2.1.14",
"sourceRepository": "jens/unraid-appops-gateway",
"sourceRevision": "b" * 40,
"sourceTree": "c" * 40,
"artifactSha256": module._artifact_digest(entries, files),
"builtAt": "2026-10-02T00:00:00.000Z",
}
entries["controller-release.json"] = (
json.dumps(manifest, separators=(",", ":")).encode(), 0o644
)
identity = module._release_identity(entries)
assert identity["artifactSha256"] == manifest["artifactSha256"]
assert module._matching_release_files(entries, manifest["artifactSha256"]) == files
current_entries = {name: (f"current:{name}".encode(), 0o644) for name in module.RELEASE_FILES}
assert module._artifact_digest(current_entries) == module._artifact_digest(
current_entries, module.RELEASE_FILES
)
del current_entries["prepare_mcp_hub_candidate.py"]
with pytest.raises(module.VolumeTransitionError, match="release_incomplete"):
module._artifact_digest(current_entries)
def test_previous_21_file_helper_manifest_remains_accepted():
files = HISTORICAL_CANDIDATE_HELPER_RELEASE_FILES
assert module.LEGACY_RELEASE_FILES_WITH_CANDIDATE_HELPER == files
assert len(files) == 21
assert files.count("prepare_mcp_hub_candidate.py") == 1
entries = {name: (f"previous-helper:{name}".encode(), 0o644) for name in files}
manifest = {
"apiVersion": "itworx.controller-artifact/v1",
"controllerVersion": "2.1.14",
"sourceRepository": "jens/unraid-appops-gateway",
"sourceRevision": "d" * 40,
"sourceTree": "e" * 40,
"artifactSha256": module._artifact_digest(entries, files),
"builtAt": "2026-10-02T00:00:00.000Z",
}
entries["controller-release.json"] = (
json.dumps(manifest, separators=(",", ":")).encode(), 0o644
)
identity = module._release_identity(entries)
assert identity["artifactSha256"] == manifest["artifactSha256"]
assert module._matching_release_files(entries, manifest["artifactSha256"]) == files
def test_candidate_preparation_helper_is_staged_archived_and_digest_bound(tmp_path):
if os.name == "nt":
pytest.skip("release staging requires POSIX descriptor-based file checks")
helper_name = "prepare_mcp_hub_candidate.py"
helper_source = ROOT / "ops" / "gitea-deploy-broker" / helper_name
helper_bytes = helper_source.read_bytes()
stage_root = tmp_path / "source"
ops = stage_root / "ops" / "gitea-deploy-broker"
scripts = stage_root / "scripts"
ops.mkdir(parents=True, mode=0o700)
scripts.mkdir(mode=0o700)
for name in module.RELEASE_FILES:
folder = scripts if name == "run_repopulse_adoption.py" else ops
contents = helper_bytes if name == helper_name else f"source:{name}\n".encode()
(folder / name).write_bytes(contents)
staged = module._stage_source_files(stage_root)
assert staged[helper_name] == (helper_bytes, 0o644)
revision = "a" * 40
tree = "b" * 40
entries, directories = module._prepare_merged_tree(
stage_root, ({}, {"": 0o755}), revision, tree, "2.1.14"
)
artifact_digest = module._release_identity(entries)["artifactSha256"]
assert artifact_digest == module._artifact_digest(entries)
archive = module._release_tar_bytes(revision, (entries, directories))
extracted, _ = module._read_tar_tree(archive)
assert extracted[helper_name][0] == helper_bytes
assert module._release_identity(extracted)["artifactSha256"] == artifact_digest
tampered = dict(extracted)
tampered[helper_name] = (helper_bytes + b"# altered after manifest\n", 0o644)
with pytest.raises(module.VolumeTransitionError, match="release_identity_invalid"):
module._release_identity(tampered)
def test_installer_explicit_archive_and_install_contract_includes_candidate_helper():
source = (ROOT / "scripts/upgrade_deployment_controller.sh").read_text(encoding="utf-8")
archive_paths = source.split("source_paths=(", 1)[1].split("\n)", 1)[0]
installed_modules = source.split("module_files=(", 1)[1].split("\n)", 1)[0]
assert "ops/gitea-deploy-broker/prepare_mcp_hub_candidate.py" in archive_paths
assert "prepare_mcp_hub_candidate.py" in installed_modules
powershell = (ROOT / "scripts/upgrade_deployment_controller.ps1").read_text(encoding="utf-8")
assert "ops/gitea-deploy-broker" in powershell.split("& git archive", 1)[1].splitlines()[0]
def test_current_release_digest_binds_the_role_password_helper():
entries = {name: (f"source:{name}".encode(), 0o644) for name in module.RELEASE_FILES}
expected = module._artifact_digest(entries)
@@ -1970,3 +2563,75 @@ def test_current_e854_crlf_exception_integrates_with_git_source_verification(mon
git_outputs[other_path] = b"tampered protected Git source"
with pytest.raises(module.VolumeTransitionError, match="source_mismatch"):
module._verify_git_source_identity(entries, identity, controller_id=controller_id)
def _current_controller_with_graph_mount():
return {
"Id": "c" * 64, "Image": IMAGE_ID, "Name": "/gitea-deploy-control",
"State": {"Running": True},
"Mounts": [
{"Type": "bind", "Source": module._expected_revision_path(REVISION).as_posix(),
"Destination": module.EXECUTABLE_TARGET, "RW": False, "Propagation": "rslave"},
{"Type": "bind", "Source": "/var/lib/docker", "Destination": "/var/lib/docker",
"RW": False, "Propagation": "rslave"},
],
}
def test_current_controller_accepts_only_fixed_readonly_docker_root_ancestor(monkeypatch):
item = _current_controller_with_graph_mount()
monkeypatch.setattr(module, "_run", lambda *_args, **_kwargs: json.dumps([item]).encode())
assert module._controller_image_inspect(IMAGE_ID) == item
@pytest.mark.parametrize("damage", ["writable", "redirected_target", "child_source", "parent_source", "volume_type", "named_volume_alias", "duplicate_graph", "shared_propagation", "private_propagation"])
def test_current_controller_rejects_other_docker_root_appdata_aliases(monkeypatch, damage):
item = _current_controller_with_graph_mount()
graph = item["Mounts"][1]
if damage == "writable":
graph["RW"] = True
elif damage == "redirected_target":
graph["Destination"] = "/graph-alias"
elif damage == "child_source":
graph["Source"] = "/var/lib/docker/volumes"
elif damage == "parent_source":
graph["Source"] = "/var/lib"
elif damage == "volume_type":
graph["Type"] = "volume"
elif damage == "named_volume_alias":
graph["Name"] = module.MANAGED_APPDATA_VOLUME
elif damage == "duplicate_graph":
item["Mounts"].append(dict(graph))
elif damage == "shared_propagation":
graph["Propagation"] = "rshared"
else:
graph["Propagation"] = "rprivate"
monkeypatch.setattr(module, "_run", lambda *_args, **_kwargs: json.dumps([item]).encode())
with pytest.raises(module.VolumeTransitionError, match="managed_appdata_mount_alias"):
module._controller_image_inspect(IMAGE_ID)
@pytest.mark.parametrize("populated", [False, True])
def test_config_snapshot_preserves_fixed_appops_legacy_intent_directory(populated):
entries = {name: (b"{}", 0o600) for name in ("projects.json", "broker-token", "gitea-validation.json", "gitea-validation-token")}
if populated:
entries["appops-legacy-upgrade/unraid-appops-gateway.json"] = (b'{"schema":"itworx.appops-legacy-upgrade-intent/v1"}', 0o600)
directories = {"": 0o755, "appops-legacy-upgrade": 0o700}
assert module._config_tree(entries, directories) == (entries, directories)
assert module._read_tar_tree(module._release_tar_bytes(REVISION, (entries, directories))) == (entries, directories)
@pytest.mark.parametrize("damage", ["unknown_file", "nested_directory", "public_directory", "public_file"])
def test_config_snapshot_keeps_appops_legacy_state_contract_closed(damage):
entries = {name: (b"{}", 0o600) for name in ("projects.json", "broker-token", "gitea-validation.json", "gitea-validation-token")}
directories = {"": 0o755, "appops-legacy-upgrade": 0o700}
if damage == "unknown_file":
entries["appops-legacy-upgrade/shell.py"] = (b"unexpected", 0o600)
elif damage == "nested_directory":
directories["appops-legacy-upgrade/nested"] = 0o700
elif damage == "public_directory":
directories["appops-legacy-upgrade"] = 0o755
else:
entries["appops-legacy-upgrade/unraid-appops-gateway.json"] = (b"{}", 0o644)
with pytest.raises(module.VolumeTransitionError):
module._config_tree(entries, directories)