Publish curated Unraid-AppOps-Gateway source from 7144ea3c4cb735745046e4e1c2d3a3e88fa1a302
This commit is contained in:
1 parent
0a82153d95
commit
e73772cdf2
4 files changed
+323
-6
No files matched your search
@@ -1,3 +1,3 @@
|
||||
# Public source export
|
||||
|
||||
This source snapshot was generated from the private canonical repository at revision `451646fee806a47e3256731766980a05a262dcdd`. It contains no private Git history or operational evidence. Changes are published from the canonical repository.
|
||||
This source snapshot was generated from the private canonical repository at revision `7144ea3c4cb735745046e4e1c2d3a3e88fa1a302`. It contains no private Git history or operational evidence. Changes are published from the canonical repository.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"sourceRevision": "451646fee806a47e3256731766980a05a262dcdd",
|
||||
"sourceRevision": "7144ea3c4cb735745046e4e1c2d3a3e88fa1a302",
|
||||
"files": [
|
||||
{
|
||||
"path": ".dockerignore",
|
||||
@@ -889,8 +889,8 @@
|
||||
},
|
||||
{
|
||||
"path": "backend/tests/test_controller_artifact_bootstrap.py",
|
||||
"sha256": "2694d9e601bb06aaabc9491c85fd7a17e796f20ea089792cb964c4a6fe93aaf6",
|
||||
"bytes": 60349
|
||||
"sha256": "da2ff9e9ec07c15f2fed076d0d7c76017496252c5584a8383d6c3132bdbaf25f",
|
||||
"bytes": 62434
|
||||
},
|
||||
{
|
||||
"path": "backend/tests/test_controller_artifact_promoter.py",
|
||||
@@ -914,8 +914,8 @@
|
||||
},
|
||||
{
|
||||
"path": "backend/tests/test_controller_hub_release_volume.py",
|
||||
"sha256": "2b89660fe44b8c5a46a4cad6b9cb68ac34f0716b4e72ac171830dcf61b0ac2c8",
|
||||
"bytes": 73499
|
||||
"sha256": "c6f8304f8f3fbbf42900c07a5b01fb40edeb3f6657849b55f9bf4fd59cdb79f6",
|
||||
"bytes": 84733
|
||||
},
|
||||
{
|
||||
"path": "backend/tests/test_controller_root_procedure_contract.py",
|
||||
|
||||
@@ -236,6 +236,14 @@ def _modern_controller_mounts(source_revision: str) -> list[dict[str, Any]]:
|
||||
"RW": False,
|
||||
"Propagation": "rprivate",
|
||||
},
|
||||
{
|
||||
"Type": "volume",
|
||||
"Name": "itworx-forge-master-key-backup",
|
||||
"Source": "/var/lib/docker/volumes/itworx-forge-master-key-backup/_data",
|
||||
"Destination": "/forge-master-key-backup",
|
||||
"RW": True,
|
||||
"Propagation": "",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -1244,6 +1252,16 @@ def test_completed_child_uses_new_health_after_controller_recreation():
|
||||
"post-volume-mode",
|
||||
"post-config-bind-source",
|
||||
"post-config-bind-mode",
|
||||
"post-code-bind-propagation",
|
||||
"post-config-bind-propagation",
|
||||
"post-missing-backup-volume",
|
||||
"post-backup-volume-type",
|
||||
"post-backup-volume-name",
|
||||
"post-backup-volume-source",
|
||||
"post-backup-volume-target",
|
||||
"post-backup-volume-mode",
|
||||
"post-backup-volume-propagation",
|
||||
"post-duplicate-backup-volume",
|
||||
"post-extra-mount",
|
||||
],
|
||||
)
|
||||
@@ -1281,6 +1299,26 @@ def test_postactivation_controller_requires_exact_compose_runtime(mutation: str)
|
||||
controller["Mounts"][9]["Source"] = "/tmp/config"
|
||||
elif mutation == "post-config-bind-mode":
|
||||
controller["Mounts"][9]["RW"] = True
|
||||
elif mutation == "post-code-bind-propagation":
|
||||
controller["Mounts"][3]["Propagation"] = "rslave"
|
||||
elif mutation == "post-config-bind-propagation":
|
||||
controller["Mounts"][9]["Propagation"] = "shared"
|
||||
elif mutation == "post-missing-backup-volume":
|
||||
controller["Mounts"].pop(10)
|
||||
elif mutation == "post-backup-volume-type":
|
||||
controller["Mounts"][10]["Type"] = "bind"
|
||||
elif mutation == "post-backup-volume-name":
|
||||
controller["Mounts"][10]["Name"] = "foreign-volume"
|
||||
elif mutation == "post-backup-volume-source":
|
||||
controller["Mounts"][10]["Source"] = "/tmp/foreign-volume"
|
||||
elif mutation == "post-backup-volume-target":
|
||||
controller["Mounts"][10]["Destination"] = "/tmp/backup"
|
||||
elif mutation == "post-backup-volume-mode":
|
||||
controller["Mounts"][10]["RW"] = False
|
||||
elif mutation == "post-backup-volume-propagation":
|
||||
controller["Mounts"][10]["Propagation"] = "rprivate"
|
||||
elif mutation == "post-duplicate-backup-volume":
|
||||
controller["Mounts"].append(dict(controller["Mounts"][10]))
|
||||
elif mutation == "post-extra-mount":
|
||||
controller["Mounts"].append(dict(controller["Mounts"][0]))
|
||||
|
||||
@@ -1296,6 +1334,14 @@ def test_postactivation_accepts_well_formed_compose_hash():
|
||||
assert bootstrap._verified_live_controller(docker, source_revision=REVISION)
|
||||
|
||||
|
||||
def test_postactivation_accepts_exact_forge_backup_mount_in_eleven_mount_inventory():
|
||||
docker = FakeDocker(_legacy_helper_spec())
|
||||
controller = _modern_controller_inspect(REVISION)
|
||||
docker.containers["gitea-deploy-control"] = controller
|
||||
|
||||
assert bootstrap._verified_live_controller(docker, source_revision=REVISION)
|
||||
|
||||
|
||||
def test_successful_child_requires_exact_source_tree_version_and_health():
|
||||
docker = FakeDocker(_legacy_helper_spec())
|
||||
git = FakeProtectedGit("")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
@@ -443,6 +444,7 @@ def test_compose_uses_protected_code_and_two_config_views(compose):
|
||||
"source": module._expected_revision_path(REVISION).as_posix(),
|
||||
"target": module.EXECUTABLE_TARGET,
|
||||
"read_only": True,
|
||||
"bind": {"propagation": "rprivate"},
|
||||
} in volumes
|
||||
assert {
|
||||
"type": "volume",
|
||||
@@ -461,6 +463,7 @@ def test_compose_uses_protected_code_and_two_config_views(compose):
|
||||
"source": module._expected_config_path().as_posix(),
|
||||
"target": module.CONFIG_TARGET,
|
||||
"read_only": True,
|
||||
"bind": {"propagation": "rprivate"},
|
||||
} in volumes
|
||||
assert len(volumes) == 5
|
||||
volumes.remove(next(item for item in volumes if item["target"] == module.EXECUTABLE_TARGET))
|
||||
@@ -487,6 +490,150 @@ def test_compose_uses_protected_code_and_two_config_views(compose):
|
||||
assert calls[0][1] == ORIGINAL_COMPOSE
|
||||
|
||||
|
||||
def test_compose_migrates_legacy_config_alias_to_explicit_private_bind(compose):
|
||||
baseline, _calls = compose
|
||||
baseline["services"][module.SERVICE]["volumes"].append(
|
||||
{
|
||||
"type": "bind",
|
||||
"source": module._expected_config_path().as_posix(),
|
||||
"target": module.CONFIG_TARGET,
|
||||
"read_only": True,
|
||||
}
|
||||
)
|
||||
|
||||
rendered = json.loads(module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID))
|
||||
aliases = [
|
||||
mount for mount in rendered["services"][module.SERVICE]["volumes"]
|
||||
if mount.get("target") == module.CONFIG_TARGET
|
||||
]
|
||||
assert aliases == [
|
||||
{
|
||||
"type": "bind",
|
||||
"source": module._expected_config_path().as_posix(),
|
||||
"target": module.CONFIG_TARGET,
|
||||
"read_only": True,
|
||||
"bind": {"propagation": "rprivate"},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_compose_accepts_empty_bind_normalization_and_canonicalizes_both_managed_binds(compose):
|
||||
baseline, _calls = compose
|
||||
volumes = baseline["services"][module.SERVICE]["volumes"]
|
||||
next(item for item in volumes if item["target"] == module.EXECUTABLE_TARGET)["bind"] = {}
|
||||
volumes.append(
|
||||
{
|
||||
"type": "bind",
|
||||
"source": module._expected_config_path().as_posix(),
|
||||
"target": module.CONFIG_TARGET,
|
||||
"read_only": True,
|
||||
"bind": {},
|
||||
}
|
||||
)
|
||||
|
||||
rendered = json.loads(module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID))
|
||||
managed = [
|
||||
mount for mount in rendered["services"][module.SERVICE]["volumes"]
|
||||
if mount.get("target") in {module.EXECUTABLE_TARGET, module.CONFIG_TARGET}
|
||||
]
|
||||
assert {item["target"]: item["bind"] for item in managed} == {
|
||||
module.EXECUTABLE_TARGET: {"propagation": "rprivate"},
|
||||
module.CONFIG_TARGET: {"propagation": "rprivate"},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("target", "bind"),
|
||||
[
|
||||
(module.EXECUTABLE_TARGET, {"propagation": "rslave"}),
|
||||
(module.EXECUTABLE_TARGET, {"propagation": "shared"}),
|
||||
(module.EXECUTABLE_TARGET, {"propagation": "rprivate", "create_host_path": False}),
|
||||
(module.CONFIG_TARGET, {"propagation": "rslave"}),
|
||||
(module.CONFIG_TARGET, {"propagation": "shared"}),
|
||||
(module.CONFIG_TARGET, {"propagation": "rprivate", "create_host_path": False}),
|
||||
],
|
||||
)
|
||||
def test_compose_rejects_noncanonical_controller_bind_propagation(compose, target, bind):
|
||||
baseline, _calls = compose
|
||||
volumes = baseline["services"][module.SERVICE]["volumes"]
|
||||
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,
|
||||
}
|
||||
)
|
||||
|
||||
with pytest.raises(module.VolumeTransitionError):
|
||||
module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID)
|
||||
|
||||
|
||||
def test_compose_accepts_already_canonical_controller_mounts_without_duplicates(compose):
|
||||
baseline, _calls = compose
|
||||
volumes = baseline["services"][module.SERVICE]["volumes"]
|
||||
next(item for item in volumes if item["target"] == module.EXECUTABLE_TARGET)["bind"] = {
|
||||
"propagation": "rprivate"
|
||||
}
|
||||
volumes.extend(
|
||||
[
|
||||
{
|
||||
"type": "volume",
|
||||
"source": module.VOLUME,
|
||||
"target": module.TARGET,
|
||||
"volume": {"nocopy": True},
|
||||
},
|
||||
{
|
||||
"type": "volume",
|
||||
"source": module.BACKUP_VOLUME,
|
||||
"target": module.BACKUP_TARGET,
|
||||
"volume": {"nocopy": True},
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": module._expected_config_path().as_posix(),
|
||||
"target": module.CONFIG_TARGET,
|
||||
"read_only": True,
|
||||
"bind": {"propagation": "rprivate"},
|
||||
},
|
||||
]
|
||||
)
|
||||
baseline["volumes"] = {
|
||||
module.VOLUME: {"name": module.VOLUME, "external": True},
|
||||
module.BACKUP_VOLUME: {"name": module.BACKUP_VOLUME, "external": True},
|
||||
}
|
||||
|
||||
rendered = json.loads(module.rendered_model(ORIGINAL_COMPOSE, REVISION, IMAGE_ID))
|
||||
final_mounts = rendered["services"][module.SERVICE]["volumes"]
|
||||
assert len(final_mounts) == len(volumes)
|
||||
assert sum(item.get("target") == module.EXECUTABLE_TARGET for item in final_mounts) == 1
|
||||
assert sum(item.get("target") == module.TARGET for item in final_mounts) == 1
|
||||
assert sum(item.get("target") == module.BACKUP_TARGET for item in final_mounts) == 1
|
||||
assert sum(item.get("target") == module.CONFIG_TARGET for item in final_mounts) == 1
|
||||
|
||||
|
||||
def test_activation_and_rollback_models_both_pin_managed_bind_propagation(compose):
|
||||
_baseline, _calls = compose
|
||||
old_model = json.loads(module.rendered_model(ORIGINAL_COMPOSE, "b" * 40, IMAGE_ID))
|
||||
next_model = json.loads(module.rendered_model(ORIGINAL_COMPOSE, "c" * 40, IMAGE_ID))
|
||||
|
||||
for rendered in (old_model, next_model):
|
||||
mounts = rendered["services"][module.SERVICE]["volumes"]
|
||||
managed = {
|
||||
mount["target"]: mount
|
||||
for mount in mounts
|
||||
if mount.get("target") in {module.EXECUTABLE_TARGET, module.CONFIG_TARGET}
|
||||
}
|
||||
assert set(managed) == {module.EXECUTABLE_TARGET, module.CONFIG_TARGET}
|
||||
assert managed[module.EXECUTABLE_TARGET]["bind"] == {"propagation": "rprivate"}
|
||||
assert managed[module.CONFIG_TARGET]["bind"] == {"propagation": "rprivate"}
|
||||
|
||||
|
||||
def test_compose_allows_only_the_fixed_managed_appdata_volume_mount(compose):
|
||||
baseline, _calls = compose
|
||||
mount = {
|
||||
@@ -1699,3 +1846,127 @@ def test_legacy_crlf_compatibility_is_limited_to_one_attested_handoff(
|
||||
):
|
||||
identity = {"sourceRevision": revision, "controllerVersion": version}
|
||||
assert module._matches_legacy_crlf_source(name, captured, source, identity) is accepted
|
||||
|
||||
|
||||
def test_current_e854_crlf_capture_requires_exact_full_release_identity():
|
||||
checked_out_source = (ROOT / "scripts/run_repopulse_adoption.py").read_bytes()
|
||||
source = checked_out_source.replace(b"\r\n", b"\n")
|
||||
captured = source.replace(b"\n", b"\r\n")
|
||||
assert hashlib.sha256(source).hexdigest() == (
|
||||
"9c66fae21f1eec27215af08f7bb7d24bafc6f10757393b56c42721fa99dccc66"
|
||||
)
|
||||
assert hashlib.sha256(captured).hexdigest() == (
|
||||
"e3036cef0f6e1934297de2d35c0381f93441bbf6f28bd18c94c3f47fc116e4e1"
|
||||
)
|
||||
identity = {
|
||||
"sourceRevision": "e854334923cca574482d22b8424ba3ce46ec6aa1",
|
||||
"sourceTree": "cf2a117f9222b952deb9902c646116612f3a7ea0",
|
||||
"controllerVersion": "2.1.14",
|
||||
"artifactSha256": "e88739bf091c8bac77ea0db5b97300666a1baa8ba984bbf6ae91e3bf25719e08",
|
||||
}
|
||||
|
||||
assert module._matches_legacy_crlf_source(
|
||||
"run_repopulse_adoption.py", captured, source, identity
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("name", "field", "value", "change_capture"),
|
||||
[
|
||||
("run_repopulse_adoption.py", "sourceRevision", "f" * 40, False),
|
||||
("run_repopulse_adoption.py", "sourceTree", "f" * 40, False),
|
||||
("run_repopulse_adoption.py", "controllerVersion", "2.1.15", False),
|
||||
("run_repopulse_adoption.py", "artifactSha256", "f" * 64, False),
|
||||
("other.py", "", "", False),
|
||||
("run_repopulse_adoption.py", "", "", True),
|
||||
],
|
||||
)
|
||||
def test_current_e854_crlf_compatibility_rejects_near_misses(
|
||||
name, field, value, change_capture,
|
||||
):
|
||||
checked_out_source = (ROOT / "scripts/run_repopulse_adoption.py").read_bytes()
|
||||
source = checked_out_source.replace(b"\r\n", b"\n")
|
||||
captured = source.replace(b"\n", b"\r\n")
|
||||
identity = {
|
||||
"sourceRevision": "e854334923cca574482d22b8424ba3ce46ec6aa1",
|
||||
"sourceTree": "cf2a117f9222b952deb9902c646116612f3a7ea0",
|
||||
"controllerVersion": "2.1.14",
|
||||
"artifactSha256": "e88739bf091c8bac77ea0db5b97300666a1baa8ba984bbf6ae91e3bf25719e08",
|
||||
}
|
||||
if field:
|
||||
identity[field] = value
|
||||
if change_capture:
|
||||
captured = captured.replace(b"\r\n", b"\n", 1)
|
||||
|
||||
assert not module._matches_legacy_crlf_source(name, captured, source, identity)
|
||||
|
||||
|
||||
def test_current_e854_crlf_exception_integrates_with_git_source_verification(monkeypatch):
|
||||
revision = "e854334923cca574482d22b8424ba3ce46ec6aa1"
|
||||
tree = "cf2a117f9222b952deb9902c646116612f3a7ea0"
|
||||
artifact_sha = "e88739bf091c8bac77ea0db5b97300666a1baa8ba984bbf6ae91e3bf25719e08"
|
||||
files = module.LEGACY_RELEASE_FILES_BEFORE_FORGE
|
||||
assert len(files) == 30
|
||||
checked_out_script = (ROOT / "scripts/run_repopulse_adoption.py").read_bytes()
|
||||
runner_source = checked_out_script.replace(b"\r\n", b"\n")
|
||||
runner_capture = runner_source.replace(b"\n", b"\r\n")
|
||||
assert hashlib.sha256(runner_source).hexdigest() == (
|
||||
"9c66fae21f1eec27215af08f7bb7d24bafc6f10757393b56c42721fa99dccc66"
|
||||
)
|
||||
assert hashlib.sha256(runner_capture).hexdigest() == (
|
||||
"e3036cef0f6e1934297de2d35c0381f93441bbf6f28bd18c94c3f47fc116e4e1"
|
||||
)
|
||||
source_by_name = {}
|
||||
entries = {}
|
||||
for name in files:
|
||||
path = (ROOT / "scripts/run_repopulse_adoption.py" if name == "run_repopulse_adoption.py"
|
||||
else ROOT / "ops/gitea-deploy-broker" / name)
|
||||
source = runner_source if name == "run_repopulse_adoption.py" else path.read_bytes()
|
||||
source = source.replace(b"\r\n", b"\n")
|
||||
source_by_name[name] = source
|
||||
entries[name] = (
|
||||
runner_capture if name == "run_repopulse_adoption.py" else source,
|
||||
0o644,
|
||||
)
|
||||
identity_data = {
|
||||
"apiVersion": "itworx.controller-artifact/v1",
|
||||
"controllerVersion": "2.1.14",
|
||||
"sourceRepository": "jens/unraid-appops-gateway",
|
||||
"sourceRevision": revision,
|
||||
"sourceTree": tree,
|
||||
"artifactSha256": artifact_sha,
|
||||
"builtAt": "2026-09-30T00:00:00Z",
|
||||
}
|
||||
entries["controller-release.json"] = (
|
||||
json.dumps(identity_data, separators=(",", ":")).encode(), 0o644
|
||||
)
|
||||
identity = {key: str(value) for key, value in identity_data.items()}
|
||||
git_outputs = {path: source for name, source in source_by_name.items() for path in [
|
||||
"scripts/run_repopulse_adoption.py"
|
||||
if name == "run_repopulse_adoption.py"
|
||||
else f"ops/gitea-deploy-broker/{name}"
|
||||
]}
|
||||
controller_id = "c" * 64
|
||||
|
||||
def select_attested_files(captured, expected):
|
||||
assert expected == artifact_sha
|
||||
assert all(name in captured for name in files)
|
||||
return files
|
||||
|
||||
def run(arguments, **_kwargs):
|
||||
assert arguments[:5] == [
|
||||
"docker", "exec", controller_id, "git", f"--git-dir={module.SOURCE_GIT_DIR}"
|
||||
]
|
||||
if arguments[-2] == "rev-parse":
|
||||
return f"{tree}\n".encode()
|
||||
return git_outputs[arguments[-1].split(":", 1)[-1]]
|
||||
|
||||
monkeypatch.setattr(module, "_matching_release_files", select_attested_files)
|
||||
monkeypatch.setattr(module, "_run", run)
|
||||
module._verify_git_source_identity(entries, identity, controller_id=controller_id)
|
||||
|
||||
other_file = next(name for name in files if name != "run_repopulse_adoption.py")
|
||||
other_path = f"ops/gitea-deploy-broker/{other_file}"
|
||||
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)
|
||||
Reference in new issue
Block a user