Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,11 @@ def run_docker_binary_transition_smoke(
chmod 0440 /etc/sudoers.d/capsemtest
check_binary_versions() {{
expected="$1"
# Each installed package is held to the binaries it owns: the older one
# predates capsem-router (it shipped capsem-port-router).
owned=$(dpkg -L capsem)
for bin in {helpers}; do
printf '%s\n' "$owned" | grep -Fqx "/usr/bin/$bin" || continue
su capsemtest -c "test -x \"\\$HOME/.capsem/bin/$bin\""
su capsemtest -c "\"\\$HOME/.capsem/bin/$bin\" --version" | grep -F "$expected"
done
Expand Down
22 changes: 20 additions & 2 deletions build_system/builder/release/tools/release_installed_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@
)


def packaged_host_binaries(deb: Path) -> tuple[str, ...]:
"""The host binaries an exact package ships, in HOST_BINARIES order.

A transition installs an older package first, and binary sets change
between releases: 0.6.3 shipped capsem-port-router, not capsem-router.
Checking the current names against it failed on a binary that package
never had, so each installed package is held to its own payload.
"""
shipped = {
Path(name).name
for name in deb_payload_files(deb, select=lambda name: Path(name).parent.as_posix().endswith("usr/bin"))
}
return tuple(binary for binary in HOST_BINARIES if binary in shipped)


def packaged_manifest_metadata(deb: Path) -> dict[str, str]:
"""Return the future polling identity declared by an exact package.

Expand Down Expand Up @@ -114,7 +129,10 @@ def exact_installed_probe_shell(evidence_dir: Path) -> str:
}}
check_binary_versions() {{
expected="$1"
for binary in {" ".join(HOST_BINARIES)}; do
artifact="$2"
binaries=$({shlex.quote(sys.executable)} -c 'import sys; from pathlib import Path; from capsem_builder.release.tools.release_installed_probe import packaged_host_binaries; print(" ".join(packaged_host_binaries(Path(sys.argv[1]))))' "$artifact")
test -n "$binaries"
for binary in $binaries; do
test -x "$CAPSEM_HOME_DIR/bin/$binary"
if [ "$binary" = capsem ]; then
"$CAPSEM_HOME_DIR/bin/$binary" version
Expand All @@ -134,7 +152,7 @@ def exact_installed_probe_shell(evidence_dir: Path) -> str:
architecture="$7"
metadata_manifest_url="${{8:-$manifest_url}}"
wait_for_service
check_binary_versions "$package_version"
check_binary_versions "$package_version" "$artifact"
dpkg-query -W -f='${{Version}}' capsem | grep -Fx "$package_version"
{shlex.quote(sys.executable)} build_system/scripts/release/verify-installed-release.py \
--capsem "$CAPSEM_BIN" \
Expand Down
80 changes: 80 additions & 0 deletions build_system/tests/release/test_deb_package_portability.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,83 @@ def test_manifest_metadata_requires_one_package_owned_member(package: Path, coun
_write_synthetic_deb(tree, package)
with pytest.raises(SystemExit, match=f"exactly one manifest metadata, found {count}"):
release_installed_probe.packaged_manifest_metadata(package)


def test_installed_probe_checks_the_binaries_the_package_ships(package: Path) -> None:
"""A transition installs an older package whose binary set differs.

0.6.3 shipped capsem-port-router (and no capsem-router); the probe checked
every current binary name against it and failed the release glow-up on a
binary that package never had. It checks what the package ships.
"""
tree = package.parent / "tree"
for name in ("capsem-service", "capsem-port-router", "capsem-app"):
(tree / "usr/bin" / name).write_bytes(b"old package binary")
package.unlink()
_write_synthetic_deb(tree, package)

assert release_installed_probe.packaged_host_binaries(package) == ("capsem", "capsem-service")


def test_installed_probe_shell_checks_each_artifact_payload(tmp_path: Path) -> None:
shell = release_installed_probe.exact_installed_probe_shell(tmp_path)
assert 'check_binary_versions "$package_version" "$artifact"' in shell
assert "packaged_host_binaries" in shell


def _public_transition_version_check(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> str:
"""The rendered public transition script's binary check, as bash."""
import re

from capsem_builder.release.tools import check_public_binary_release as public

scripts: list[str] = []
monkeypatch.setattr(public.shutil, "which", lambda _name: "/usr/bin/docker")
monkeypatch.setattr(
public, "linux_amd64_current_package", lambda manifest: {"version": manifest["version"]}
)
monkeypatch.setattr(public.subprocess, "run", lambda argv, **_: scripts.append(argv[-1]))
public.run_docker_binary_transition_smoke(
older_manifest={"version": "0.6.3"},
newer_manifest={"version": "0.6.4"},
install_script_url="http://127.0.0.1:1/install.sh",
docker_image="unused",
work_dir=tmp_path / "work",
)
monkeypatch.undo() # the stubs patched the shared subprocess module
match = re.search(r"^check_binary_versions\(\) \{\n.*?^\}\n", scripts[0], re.S | re.M)
assert match, scripts[0]
return match.group(0)


@pytest.mark.parametrize("owned_router", [False, True])
def test_public_transition_checks_the_binaries_the_installed_package_owns(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, owned_router: bool
) -> None:
"""The older public package predates capsem-router; checking the current
binary names against it failed a release on a binary it never shipped.
A binary the installed package does own must still be present."""
import subprocess

function = _public_transition_version_check(tmp_path, monkeypatch)
home = tmp_path / "home"
(home / ".capsem/bin").mkdir(parents=True)
for name in ("capsem", "capsem-service"):
tool = home / ".capsem/bin" / name
tool.write_text("#!/bin/sh\necho " + name + " 0.6.3\n")
tool.chmod(0o755)
owned = ["/usr/bin/capsem", "/usr/bin/capsem-service"] + (["/usr/bin/capsem-router"] if owned_router else [])
stubs = tmp_path / "stubs"
stubs.mkdir()
(stubs / "dpkg").write_text("#!/bin/sh\nprintf '%s\\n' " + " ".join(owned) + "\n")
(stubs / "su").write_text('#!/bin/sh\nshift; exec sh -c "$2"\n')
for stub in stubs.iterdir():
stub.chmod(0o755)
result = subprocess.run(
["bash", "-c", "set -euo pipefail\n" + function + "\ncheck_binary_versions 0.6.3\n"],
env={"PATH": f"{stubs}:/usr/bin:/bin", "HOME": str(home)},
capture_output=True,
text=True,
check=False,
)
assert (result.returncode == 0) is (not owned_router), result.stderr
2 changes: 1 addition & 1 deletion build_system/tests/release/test_release_glowup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1659,7 +1659,7 @@ def capture(command: list[str], **_kwargs) -> None:
assert "build_system/scripts/release/verify-installed-release.py" in script
assert '"$CAPSEM_BIN" doctor' in script
assert "build_system/scripts/build/run-installed-winterfell.py" in script
assert "capsem-mock-server" in script
assert "packaged_host_binaries" in script # each package holds itself to its own payload
assert "update --yes" not in script
assert "update --yes" not in tamper_script
assert "update --yes" not in incompatible_script
Expand Down
Loading