Compare commits

..

No commits in common. "f21bac7d1c137c745758330235b776dfbe94856d" and "528176ad8243b88525a666443cc72a660564a859" have entirely different histories.

5 changed files with 7 additions and 162 deletions

View file

@ -6,10 +6,9 @@
* Use `no_log` on systemd unit interrogations to suppress potential sensitive output when applying Ansible * Use `no_log` on systemd unit interrogations to suppress potential sensitive output when applying Ansible
* Support manifesting Puppet code, as well as Ansible! * Support manifesting Puppet code, as well as Ansible!
* Support manifesting Salt code, as well as Ansible and Puppet! * Support manifesting Salt code, as well as Ansible and Puppet!
* Take advantage of Jinjaturtle 0.5.5 if it's present, to render .erb templates for Puppet (as well as j2 for Ansible and Salt)
* A lot of under-the-bonnet refactoring to make it easier to extend to cover other config managers (that don't suck) in future. * A lot of under-the-bonnet refactoring to make it easier to extend to cover other config managers (that don't suck) in future.
* Support for detecting Docker and Podman images and enforcing their presence (by SHA256 hash). * Support for detecting Docker images.
* Add support for detecting Flatpaks and Snaps. * Add support for detecting Flatpaks and Snaps (manifests for Ansible code only, not Puppet or Salt at this time)
# 0.6.0 # 0.6.0

View file

@ -28,6 +28,5 @@ def get_enroll_version() -> str:
for dist in [*dist_names, "enroll"]: for dist in [*dist_names, "enroll"]:
try: try:
return version(dist) return version(dist)
except Exception: # nosec B112 except Exception:
continue return "unknown"
return "unknown"

View file

@ -7,6 +7,7 @@ filedust -y .
# Publish to Pypi # Publish to Pypi
poetry build poetry build
poetry publish
# Make AppImage # Make AppImage
poetry run pyproject-appimage --output dist/Enroll.AppImage poetry run pyproject-appimage --output dist/Enroll.AppImage
@ -85,9 +86,6 @@ for dist in ${DISTS[@]}; do
qubes-gpg-client --local-user "$KEYID" --detach-sign --armor "$RPM_REPO/repodata/repomd.xml" > "$RPM_REPO/repodata/repomd.xml.asc" qubes-gpg-client --local-user "$KEYID" --detach-sign --armor "$RPM_REPO/repodata/repomd.xml" > "$RPM_REPO/repodata/repomd.xml.asc"
done done
# If we got this far, publish to Poetry too
poetry publish
echo "==> Syncing repo to server..." echo "==> Syncing repo to server..."
rsync -aHPvz --exclude=.git --delete "$REPO_ROOT/" "$REMOTE/" rsync -aHPvz --exclude=.git --delete "$REPO_ROOT/" "$REMOTE/"

View file

@ -141,107 +141,3 @@ def test_container_images_collector_records_unpullable_tagged_images(
assert result.images[0]["pull_ref"] is None assert result.images[0]["pull_ref"] is None
assert "exact digest-pinned pull cannot be rendered" in result.images[0]["notes"][0] assert "exact digest-pinned pull cannot be rendered" in result.images[0]["notes"][0]
def test_container_images_collector_notes_list_exceptions(monkeypatch, tmp_path):
from enroll.harvest_collectors import container_images as ci
from enroll.harvest_collectors.container_images import ContainerImagesCollector
monkeypatch.setattr(
ci.shutil,
"which",
lambda cmd: f"/usr/bin/{cmd}" if cmd == "docker" else None,
)
def boom(_argv, *, timeout=20):
raise RuntimeError("socket unavailable")
monkeypatch.setattr(ci, "_run_command", boom)
result = ContainerImagesCollector(_context(tmp_path)).collect()
assert result.images == []
assert "Failed to list docker images" in result.notes[0]
def test_container_images_collector_notes_list_nonzero_without_detail(
monkeypatch, tmp_path
):
import subprocess
from enroll.harvest_collectors import container_images as ci
from enroll.harvest_collectors.container_images import ContainerImagesCollector
monkeypatch.setattr(
ci.shutil,
"which",
lambda cmd: f"/usr/bin/{cmd}" if cmd == "podman" else None,
)
monkeypatch.setattr(
ci,
"_run_command",
lambda argv, *, timeout=20: subprocess.CompletedProcess(argv, 42, "", ""),
)
result = ContainerImagesCollector(_context(tmp_path)).collect()
assert result.images == []
assert "exit 42" in result.notes[0]
def test_container_images_collector_notes_bad_inspect_json(monkeypatch, tmp_path):
import subprocess
from enroll.harvest_collectors import container_images as ci
from enroll.harvest_collectors.container_images import ContainerImagesCollector
image_id = "sha256:" + "d" * 64
monkeypatch.setattr(
ci.shutil,
"which",
lambda cmd: f"/usr/bin/{cmd}" if cmd == "docker" else None,
)
def fake_run(argv, *, timeout=20):
if argv[:4] == ["/usr/bin/docker", "image", "ls", "-q"]:
return subprocess.CompletedProcess(argv, 0, image_id + "\n", "")
if argv[:3] == ["/usr/bin/docker", "image", "inspect"]:
return subprocess.CompletedProcess(argv, 0, "not json", "")
raise AssertionError(argv)
monkeypatch.setattr(ci, "_run_command", fake_run)
result = ContainerImagesCollector(_context(tmp_path)).collect()
assert result.images == []
assert "Failed to parse docker image inspect JSON" in result.notes[0]
def test_container_images_collector_notes_unexpected_inspect_shape(
monkeypatch, tmp_path
):
import subprocess
from enroll.harvest_collectors import container_images as ci
from enroll.harvest_collectors.container_images import ContainerImagesCollector
image_id = "sha256:" + "e" * 64
monkeypatch.setattr(
ci.shutil,
"which",
lambda cmd: f"/usr/bin/{cmd}" if cmd == "docker" else None,
)
def fake_run(argv, *, timeout=20):
if argv[:4] == ["/usr/bin/docker", "image", "ls", "-q"]:
return subprocess.CompletedProcess(argv, 0, image_id + "\n", "")
if argv[:3] == ["/usr/bin/docker", "image", "inspect"]:
return subprocess.CompletedProcess(argv, 0, '{"not":"a-list"}', "")
raise AssertionError(argv)
monkeypatch.setattr(ci, "_run_command", fake_run)
result = ContainerImagesCollector(_context(tmp_path)).collect()
assert result.images == []
assert "Unexpected docker image inspect JSON shape" in result.notes[0]

View file

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import importlib.metadata as metadata # The version module is hard to test fully because it uses importlib.metadata
# which is difficult to mock. We'll test what we can.
def test_get_enroll_version_returns_string(): def test_get_enroll_version_returns_string():
@ -9,51 +10,3 @@ def test_get_enroll_version_returns_string():
result = get_enroll_version() result = get_enroll_version()
assert isinstance(result, str) assert isinstance(result, str)
assert len(result) > 0 assert len(result) > 0
def test_get_enroll_version_falls_back_after_bad_mapped_dist(monkeypatch):
from enroll.version import get_enroll_version
monkeypatch.setattr(
metadata,
"packages_distributions",
lambda: {"enroll": ["wrong-dist", "enroll"]},
)
def fake_version(name):
if name == "enroll":
return "0.7.0"
raise metadata.PackageNotFoundError(name)
monkeypatch.setattr(metadata, "version", fake_version)
assert get_enroll_version() == "0.7.0"
def test_get_enroll_version_uses_default_when_mapping_fails(monkeypatch):
from enroll.version import get_enroll_version
def broken_mapping():
raise RuntimeError("metadata unavailable")
monkeypatch.setattr(metadata, "packages_distributions", broken_mapping)
monkeypatch.setattr(
metadata,
"version",
lambda name: "0.7.0" if name == "enroll" else "bad",
)
assert get_enroll_version() == "0.7.0"
def test_get_enroll_version_returns_unknown_when_all_lookups_fail(monkeypatch):
from enroll.version import get_enroll_version
monkeypatch.setattr(metadata, "packages_distributions", lambda: {"enroll": ["bad"]})
def missing(_name):
raise metadata.PackageNotFoundError(_name)
monkeypatch.setattr(metadata, "version", missing)
assert get_enroll_version() == "unknown"