mirror of
https://github.com/d9pouces/id_configurator.git
synced 2026-11-09 01:47:21 +01:00
feat: update version to 2026.9.7 in pyproject.toml
Signed-off-by: d9pouces <github@19pouces.net>
This commit is contained in:
@@ -8,10 +8,10 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories:
|
||||
- url: https://host.docker.internal:{{ 'APK_REPO_PORT' | env }}/
|
||||
- name: demo
|
||||
url: https://host.docker.internal:{{ 'APK_REPO_PORT' | env }}/
|
||||
key: |
|
||||
{{ 'dockerfiles/repos/040d0af5f97a0a44.rsa.pub' | file | indent(8, first=true) }}
|
||||
yum:
|
||||
|
||||
@@ -13,8 +13,8 @@ configurations:
|
||||
uris:
|
||||
- https://host.docker.internal:{{ 'APT_REPO_PORT' | env }}/
|
||||
suites: ["./"]
|
||||
trusted: true
|
||||
keys: []
|
||||
signed-by: |
|
||||
{{ 'dockerfiles/repos/apt-signing-key.pub.asc' | file | indent(8, first=true) }}
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -8,7 +8,6 @@ configurations:
|
||||
{{ 'certs/test-ca.crt.pem' | file | indent(6, first=true) }}
|
||||
apt:
|
||||
sources: []
|
||||
keys: []
|
||||
apk:
|
||||
repositories: []
|
||||
yum:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
FROM debian:trixie-slim AS target
|
||||
FROM interdiode/id-configurator:latest AS configuration
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends gnupg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=target / /src/
|
||||
COPY --from=generated-config Apt.yaml /config.yaml
|
||||
RUN id-configurator apply --config /config.yaml --src /src --dst /dst
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
# Minimal flat-format APT repository serving one hand-built .deb package over
|
||||
# TLS, used by tox-docker to exercise the `apt` module integration test
|
||||
# (tests/configs/Apt.yaml.tmpl, tests/dockerfiles/Apt.Dockerfile).
|
||||
# (tests/configs/Apt.yaml.j2, tests/dockerfiles/Apt.Dockerfile). Its
|
||||
# InRelease file is genuinely signed with the dedicated key pair
|
||||
# apt-signing-key.key.asc / apt-signing-key.pub.asc, whose public half is
|
||||
# what tests/configs/Apt.yaml.j2 embeds as the configured source's
|
||||
# `signed-by`: the `apt` module dearmors it into a keyring, and APT itself
|
||||
# verifies the InRelease signature against that keyring on `apt-get update`
|
||||
# - no `Trusted: yes` bypass involved.
|
||||
#
|
||||
# Built by tox-docker, whose build context is this directory (not the repo
|
||||
# root) - every COPY source below must live under tests/dockerfiles/repos/.
|
||||
FROM debian:trixie-slim AS packagebuild
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends dpkg-dev \
|
||||
&& apt-get install -y --no-install-recommends dpkg-dev apt-utils gnupg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN set -eux; \
|
||||
pkg=idconfiguratortestpkg; \
|
||||
@@ -18,6 +24,13 @@ RUN set -eux; \
|
||||
WORKDIR /srv
|
||||
RUN dpkg-scanpackages --multiversion . > Packages \
|
||||
&& gzip -9c Packages > Packages.gz
|
||||
COPY apt-signing-key.key.asc /tmp/apt-signing-key.key.asc
|
||||
RUN gnupg_home="$(mktemp -d)" \
|
||||
&& gpg --homedir "$gnupg_home" --batch --import /tmp/apt-signing-key.key.asc \
|
||||
&& key_id="$(gpg --homedir "$gnupg_home" --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}')" \
|
||||
&& apt-ftparchive release . > Release \
|
||||
&& gpg --homedir "$gnupg_home" --batch --yes --pinentry-mode loopback --default-key "$key_id" --clearsign -o InRelease Release \
|
||||
&& rm -rf "$gnupg_home" /tmp/apt-signing-key.key.asc
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=packagebuild /srv /srv
|
||||
|
||||
+2
-1
@@ -14,10 +14,11 @@ CONFIG_DATA = {
|
||||
"apk": {
|
||||
"repositories": [
|
||||
{
|
||||
"name": "main",
|
||||
"url": "https://example.invalid/v3.19/main",
|
||||
"key": KEY_BYTES,
|
||||
},
|
||||
{"url": "https://example.invalid/v3.19/community"},
|
||||
{"name": "community", "url": "https://example.invalid/v3.19/community"},
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
+140
-12
@@ -1,7 +1,15 @@
|
||||
import subprocess
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from id_configurator.config import Configuration
|
||||
from id_configurator.context import Context
|
||||
from id_configurator.modules.apt import AptModule
|
||||
|
||||
KEY_CONTENT = "fake-armored-gpg-key"
|
||||
KEYRING_BYTES = b"fake-dearmored-keyring-bytes"
|
||||
|
||||
SOURCE_CONTENT = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
@@ -12,13 +20,7 @@ SOURCE_CONTENT = {
|
||||
"name": "interdiode",
|
||||
"uris": ["http://example.invalid/debian"],
|
||||
"suites": ["stable"],
|
||||
"signed-by": "/etc/apt/keyrings/interdiode.asc",
|
||||
}
|
||||
],
|
||||
"keys": [
|
||||
{
|
||||
"name": "interdiode",
|
||||
"content": "fake-key-bytes",
|
||||
"signed-by": KEY_CONTENT,
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -30,6 +32,36 @@ def make_config() -> Configuration:
|
||||
return Configuration.model_validate(SOURCE_CONTENT)
|
||||
|
||||
|
||||
def make_config_with(source_extra: dict) -> Configuration:
|
||||
data = {
|
||||
"uuid": "12345678-1234-5678-1234-567812345678",
|
||||
"organisation": "InterDiode",
|
||||
"configurations": {
|
||||
"apt": {
|
||||
"sources": [
|
||||
{
|
||||
"name": "interdiode",
|
||||
"uris": ["http://example.invalid/debian"],
|
||||
"suites": ["stable"],
|
||||
**source_extra,
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
return Configuration.model_validate(data)
|
||||
|
||||
|
||||
def _mock_gpg_dearmor_success(monkeypatch, output: bytes = KEYRING_BYTES) -> MagicMock:
|
||||
mock_run = MagicMock(
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=[], returncode=0, stdout=output, stderr=b""
|
||||
)
|
||||
)
|
||||
monkeypatch.setattr("id_configurator.modules.apt.subprocess.run", mock_run)
|
||||
return mock_run
|
||||
|
||||
|
||||
def test_is_relevant_false_without_src(tmp_path) -> None:
|
||||
context = Context(config=make_config(), dst=tmp_path / "dst", src=None)
|
||||
assert AptModule().is_relevant(context) is False
|
||||
@@ -49,7 +81,10 @@ def test_is_relevant_true_with_apt_dir(tmp_path) -> None:
|
||||
assert AptModule().is_relevant(context) is True
|
||||
|
||||
|
||||
def test_apply_disables_existing_sources_and_adds_configured_ones(tmp_path) -> None:
|
||||
def test_apply_disables_existing_sources_and_adds_configured_ones(
|
||||
tmp_path, monkeypatch
|
||||
) -> None:
|
||||
mock_run = _mock_gpg_dearmor_success(monkeypatch)
|
||||
src = tmp_path / "src"
|
||||
apt_dir = src / "etc/apt"
|
||||
sources_list_d = apt_dir / "sources.list.d"
|
||||
@@ -98,19 +133,22 @@ def test_apply_disables_existing_sources_and_adds_configured_ones(tmp_path) -> N
|
||||
|
||||
assert not (dst / "etc/apt/sources.list.d/ignored.txt").exists()
|
||||
|
||||
keyring = (dst / "etc/apt/keyrings/interdiode.asc").read_bytes()
|
||||
assert keyring == b"fake-key-bytes"
|
||||
keyring = (dst / "etc/apt/keyrings/interdiode.gpg").read_bytes()
|
||||
assert keyring == KEYRING_BYTES
|
||||
mock_run.assert_called_once()
|
||||
assert mock_run.call_args.kwargs["input"] == KEY_CONTENT.encode("utf-8")
|
||||
|
||||
configured_source = (dst / "etc/apt/sources.list.d/interdiode.sources").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert configured_source == (
|
||||
"Types: deb\nURIs: http://example.invalid/debian\nSuites: stable\n"
|
||||
"Signed-By: /etc/apt/keyrings/interdiode.asc\nEnabled: yes\n"
|
||||
"Signed-By: /etc/apt/keyrings/interdiode.gpg\nEnabled: yes\n"
|
||||
)
|
||||
|
||||
|
||||
def test_apply_without_existing_top_level_sources_list(tmp_path) -> None:
|
||||
def test_apply_without_existing_top_level_sources_list(tmp_path, monkeypatch) -> None:
|
||||
_mock_gpg_dearmor_success(monkeypatch)
|
||||
src = tmp_path / "src"
|
||||
(src / "etc/apt").mkdir(parents=True)
|
||||
dst = tmp_path / "dst"
|
||||
@@ -120,3 +158,93 @@ def test_apply_without_existing_top_level_sources_list(tmp_path) -> None:
|
||||
|
||||
assert not (dst / "etc/apt/sources.list").exists()
|
||||
assert (dst / "etc/apt/sources.list.d/interdiode.sources").exists()
|
||||
|
||||
|
||||
def test_apply_uses_signed_by_file_when_no_key_content(tmp_path, monkeypatch) -> None:
|
||||
mock_run = _mock_gpg_dearmor_success(monkeypatch)
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(
|
||||
config=make_config_with({"signed-by-file": "/etc/apt/keyrings/existing.gpg"}),
|
||||
dst=dst,
|
||||
src=None,
|
||||
)
|
||||
|
||||
AptModule().apply(context)
|
||||
|
||||
mock_run.assert_not_called()
|
||||
assert not (dst / "etc/apt/keyrings").exists()
|
||||
configured_source = (dst / "etc/apt/sources.list.d/interdiode.sources").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert configured_source == (
|
||||
"Types: deb\nURIs: http://example.invalid/debian\nSuites: stable\n"
|
||||
"Signed-By: /etc/apt/keyrings/existing.gpg\nEnabled: yes\n"
|
||||
)
|
||||
|
||||
|
||||
def test_apply_signed_by_takes_precedence_over_signed_by_file(
|
||||
tmp_path, monkeypatch
|
||||
) -> None:
|
||||
mock_run = _mock_gpg_dearmor_success(monkeypatch)
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(
|
||||
config=make_config_with(
|
||||
{
|
||||
"signed-by": KEY_CONTENT,
|
||||
"signed-by-file": "/etc/apt/keyrings/existing.gpg",
|
||||
}
|
||||
),
|
||||
dst=dst,
|
||||
src=None,
|
||||
)
|
||||
|
||||
AptModule().apply(context)
|
||||
|
||||
mock_run.assert_called_once()
|
||||
assert (dst / "etc/apt/keyrings/interdiode.gpg").read_bytes() == KEYRING_BYTES
|
||||
configured_source = (dst / "etc/apt/sources.list.d/interdiode.sources").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert "Signed-By: /etc/apt/keyrings/interdiode.gpg\n" in configured_source
|
||||
|
||||
|
||||
def test_apply_without_signed_by_writes_no_keyring_or_field(
|
||||
tmp_path, monkeypatch
|
||||
) -> None:
|
||||
mock_run = _mock_gpg_dearmor_success(monkeypatch)
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config_with({}), dst=dst, src=None)
|
||||
|
||||
AptModule().apply(context)
|
||||
|
||||
mock_run.assert_not_called()
|
||||
assert not (dst / "etc/apt/keyrings").exists()
|
||||
configured_source = (dst / "etc/apt/sources.list.d/interdiode.sources").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
assert configured_source == (
|
||||
"Types: deb\nURIs: http://example.invalid/debian\nSuites: stable\nEnabled: yes\n"
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_signed_by_raises_when_gpg_not_on_path(tmp_path, monkeypatch) -> None:
|
||||
monkeypatch.setattr("id_configurator.modules.apt.shutil.which", lambda _: None)
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(), dst=dst, src=None)
|
||||
|
||||
with pytest.raises(RuntimeError, match="gpg is not available"):
|
||||
AptModule().apply(context)
|
||||
|
||||
|
||||
def test_resolve_signed_by_raises_on_invalid_key(tmp_path, monkeypatch) -> None:
|
||||
mock_run = MagicMock(
|
||||
return_value=subprocess.CompletedProcess(
|
||||
args=[], returncode=2, stdout=b"", stderr=b"gpg: no valid data found"
|
||||
)
|
||||
)
|
||||
monkeypatch.setattr("id_configurator.modules.apt.subprocess.run", mock_run)
|
||||
dst = tmp_path / "dst"
|
||||
context = Context(config=make_config(), dst=dst, src=None)
|
||||
|
||||
with pytest.raises(RuntimeError, match="invalid GPG key for apt source interdiode"):
|
||||
AptModule().apply(context)
|
||||
|
||||
@@ -34,7 +34,6 @@ FULL_CONFIG = {
|
||||
"signed-by": "/etc/apt/keyrings/interdiode.asc",
|
||||
}
|
||||
],
|
||||
"keys": [{"name": "interdiode", "content": "ZmFrZQ=="}],
|
||||
},
|
||||
"yum": {
|
||||
"repositories": [
|
||||
@@ -76,7 +75,6 @@ def test_parse_configuration_from_json() -> None:
|
||||
configuration.configurations.apt.sources[0].signed_by
|
||||
== "/etc/apt/keyrings/interdiode.asc"
|
||||
)
|
||||
assert configuration.configurations.apt.keys[0].name == "interdiode"
|
||||
assert configuration.configurations.yum.keys[0].name == "interdiode"
|
||||
|
||||
|
||||
@@ -191,7 +189,6 @@ def test_apt_source_to_deb822_full() -> None:
|
||||
enabled=False,
|
||||
trusted=True,
|
||||
**{
|
||||
"signed-by": "/etc/apt/keyrings/interdiode.asc",
|
||||
"by-hash": True,
|
||||
"allow-insecure": False,
|
||||
"allow-weak": False,
|
||||
@@ -205,7 +202,7 @@ def test_apt_source_to_deb822_full() -> None:
|
||||
pdiffs=True,
|
||||
description="InterDiode APT repository",
|
||||
)
|
||||
rendered = source.to_deb822()
|
||||
rendered = source.to_deb822(signed_by_path="/etc/apt/keyrings/interdiode.asc")
|
||||
assert rendered.splitlines() == [
|
||||
"Types: deb deb-src",
|
||||
"URIs: http://example.invalid/debian",
|
||||
|
||||
Reference in New Issue
Block a user