lxc: code cleanup and docs polish from review

Code:
- Fix check_ssh_config_include() to do a case-insensitive line match
  (read_text().splitlines() instead of filter(None, map(..., open()))).
- Drop the default help_text= from _add_name_args(); callers now must
  supply an explicit string.
- Expand the DNSContainer class docstring.
- Fix sysctl comment: note that incus provides net.* virtualization
  so the sysctl only affects the container's network namespace.

Docs (doc/source/lxc.rst):
- Remove double blank line after page title; fix missing comma.
- Replace the plain-text root-access note with a .. caution:: block.
- Tighten the Quick-start section and lxc-test CLI entry.
This commit is contained in:
holger krekel
2026-03-08 18:12:49 +01:00
parent e2ec0cf2c5
commit 04ac2cf700
4 changed files with 36 additions and 44 deletions

View File

@@ -430,14 +430,8 @@ def _deploy_status(ct, local_hash, ix):
return f"IN-SYNC ({short})"
def _add_name_args(parser, help_text=None):
"""Add optional positional NAME arguments."""
parser.add_argument(
"names",
nargs="*",
metavar="NAME",
help=help_text or "Relay name(s) to operate on.",
)
def _add_name_args(parser, help_text):
parser.add_argument("names", nargs="*", metavar="NAME", help=help_text)
def _run_cmdeploy(subcmd, ct, ix, out, extra=None, **kwargs):

View File

@@ -95,8 +95,9 @@ class Incus:
user_ssh_config = Path.home() / ".ssh" / "config"
if not user_ssh_config.exists():
return False
lines = filter(None, map(str.strip, user_ssh_config.open("r")))
return f"Include {self.ssh_config_path}" in lines
lines = user_ssh_config.read_text().splitlines()
target = f"include {self.ssh_config_path}".lower()
return any(line.strip().lower() == target for line in lines)
def run(self, args, check=True, capture=True, input=None):
"""Run an incus command.
@@ -190,7 +191,7 @@ class Incus:
def delete_images(self):
"""Delete the cached base and relay images."""
for alias in (RELAY_IMAGE_ALIAS, BASE_IMAGE_ALIAS):
self.run(["image", "delete", alias], check=False)
self.run(["image", "delete", alias], check=False) # ok if absent
def list_managed(self):
"""Return list of dicts with name, ip, ipv6, domain, status, memory_usage."""
@@ -433,7 +434,8 @@ class RelayContainer(Container):
def disable_ipv6(self):
"""Disable IPv6 inside the container via sysctl."""
# incus provides net.* virtalization
# incus provides net.* virtualization for LXC containers so that
# these sysctls only affect the container's network namespace.
self.bash("""\
sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1
@@ -485,7 +487,7 @@ class RelayContainer(Container):
return shell(cmd, timeout=15).returncode == 0
def configure_dns(self, dns_ip):
"""Point this container's resolver at *dns_ip* and verify it works."""
"""Point this container's resolver at *dns_ip* and verify DNS is reachable."""
self.bash(f"""\
systemctl disable --now systemd-resolved 2>/dev/null || true
rm -f /etc/resolv.conf
@@ -535,7 +537,11 @@ class RelayContainer(Container):
class DNSContainer(Container):
"""Container handle for the PowerDNS name server."""
"""Container handle for the PowerDNS name server.
Manages the authoritative and recursive DNS services required for
name resolution in the local testing environment.
"""
def __init__(self, incus):
super().__init__(incus, DNS_CONTAINER_NAME, domain=DNS_DOMAIN)

View File

@@ -12,7 +12,7 @@ from cmdeploy.util import Out
pytestmark = pytest.mark.skipif(
not shutil.which("incus"),
reason="incus/lxc not installed",
reason="incus not installed",
)

View File

@@ -1,21 +1,15 @@
Local testing with LXC/Incus
============================
.. warning::
cmdeploy LXC support is geared towards local testing and CI, only.
Do not base production setups on it.
The ``cmdeploy`` tool includes support for running
chatmail relays inside local
`Incus <https://linuxcontainers.org/incus/>`_ LXC containers.
This is useful for development, testing, and CI
This is meant for development, testing, and CI
without requiring a remote server.
LXC system containers behave like lightweight virtual machines.
They share the host's kernel but run their own init system
(systemd), package manager, and network stack,
so the cmdeploy deployment scripts work exactly
LXC system containers are lightweight virtual machines
that share the host's kernel but run their own init system,
package manager, and network stack,
so the cmdeploy deployment scripts work pretty much
as they would on a real Debian server or cloud VPS.
Prerequisites
@@ -32,6 +26,16 @@ After installing incus, initialise and grant yourself access::
sudo incus admin init --minimal
sudo usermod -aG incus-admin $USER
.. caution::
Adding yourself to ``incus-admin`` grants effective root access
to the host: any member can mount host directories into a container
and manipulate them as root.
This is fine for local testing of your own relay branches,
but do **not** use it for production setups
or for testing untrusted relay branches from others.
.. warning::
You **must now log out and back in** (or run ``newgrp incus-admin``)
@@ -53,11 +57,13 @@ Quick start
source venv/bin/activate # activate venv
cmdeploy lxc-test # create containers, deploy, test
The ``lxc-test`` command executes each ``cmdeploy`` subprocess command
so you can copy-paste and run them individually.
The ``lxc-test`` command provides an automated way
to run the full deployment and test pipeline.
It executes several ``cmdeploy`` subcommands in sequential steps.
If a step fails, you can copy-paste the printed command
and run it manually to debug.
No host DNS delegation or ``~/.ssh/config`` changes are needed
because lxc-test passes ssh-related CLI options to "cmdeploy run" and "cmdeploy test" commands.
because ``lxc-test`` passes the required SSH and DNS options directly.
CLI reference
@@ -86,20 +92,6 @@ CLI reference
User containers are **never** destroyed unless named explicitly.
``lxc-test [--one]``
Idempotent full pipeline:
1. ``lxc-start``: create ``test0`` + ``test1``
containers, minimal DNS
2. ``cmdeploy run``: deploy chatmail services on each relay
3. locally cache ``localchat-relay`` image after first successful deploy
4. ``cmdeploy dns --zonefile``: generate standard
BIND-format zone files, load full DNS records
5. ``cmdeploy test``: run full test suite
By default creates, deploys, and tests both ``test0`` and ``test1``
for dual-domain federation testing (sets ``CHATMAIL_DOMAIN2=_test1.localchat``).
test0 runs dual-stack (IPv4 + IPv6) while test1 runs IPv4-only (``disable_ipv6 = True``).