mirror of
https://github.com/chatmail/relay.git
synced 2026-06-26 05:21:06 +00:00
feat(cmdeploy): add remove command
This commit is contained in:
@@ -104,7 +104,9 @@ def run_cmd(args, out):
|
|||||||
args.dns_check_disabled = True
|
args.dns_check_disabled = True
|
||||||
if not args.dns_check_disabled:
|
if not args.dns_check_disabled:
|
||||||
remote_data = dns.get_initial_remote_data(sshexec, args.config.mail_domain)
|
remote_data = dns.get_initial_remote_data(sshexec, args.config.mail_domain)
|
||||||
if not dns.check_initial_remote_data(remote_data, strict_tls=strict_tls, print=out.red):
|
if not dns.check_initial_remote_data(
|
||||||
|
remote_data, strict_tls=strict_tls, print=out.red
|
||||||
|
):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
@@ -127,7 +129,11 @@ def run_cmd(args, out):
|
|||||||
out.check_call(cmd, env=env)
|
out.check_call(cmd, env=env)
|
||||||
if args.website_only:
|
if args.website_only:
|
||||||
out.green("Website deployment completed.")
|
out.green("Website deployment completed.")
|
||||||
elif not args.dns_check_disabled and strict_tls and not remote_data["acme_account_url"]:
|
elif (
|
||||||
|
not args.dns_check_disabled
|
||||||
|
and strict_tls
|
||||||
|
and not remote_data["acme_account_url"]
|
||||||
|
):
|
||||||
out.red("Deploy completed but letsencrypt not configured")
|
out.red("Deploy completed but letsencrypt not configured")
|
||||||
out.red("Run 'cmdeploy run' again")
|
out.red("Run 'cmdeploy run' again")
|
||||||
elif args.config.ipv4_relay:
|
elif args.config.ipv4_relay:
|
||||||
@@ -141,6 +147,88 @@ def run_cmd(args, out):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def remove_cmd_options(parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"--dry-run",
|
||||||
|
dest="dry_run",
|
||||||
|
action="store_true",
|
||||||
|
help="show what would be removed without modifying the server",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--yes",
|
||||||
|
"-y",
|
||||||
|
dest="yes",
|
||||||
|
action="store_true",
|
||||||
|
help="do not ask for confirmation before removing chatmail data",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--keep-packages",
|
||||||
|
action="store_true",
|
||||||
|
help="remove chatmail files and users but do not purge installed packages",
|
||||||
|
)
|
||||||
|
add_ssh_host_option(parser)
|
||||||
|
|
||||||
|
|
||||||
|
def _confirm_remove(args, out):
|
||||||
|
if args.yes:
|
||||||
|
return True
|
||||||
|
|
||||||
|
domain = args.config.mail_domain_bare
|
||||||
|
out.red(
|
||||||
|
"WARNING: this removes chatmail services, configuration, package state, "
|
||||||
|
f"and mailbox data for {domain!r}."
|
||||||
|
)
|
||||||
|
if args.config.tls_cert_mode == "acme":
|
||||||
|
out.red(
|
||||||
|
"DNS records are not removed. If your DNS has a CAA record with "
|
||||||
|
"a Let's Encrypt accounturi, remove or relax it before deploying "
|
||||||
|
"this relay again."
|
||||||
|
)
|
||||||
|
out.red("Type the chatmail domain to continue.")
|
||||||
|
try:
|
||||||
|
answer = input(f"remove {domain}: ")
|
||||||
|
except EOFError:
|
||||||
|
answer = ""
|
||||||
|
|
||||||
|
if answer != domain:
|
||||||
|
out.red("Remove cancelled.")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def remove_cmd(args, out):
|
||||||
|
"""Remove chatmail services, configuration, state, users and packages."""
|
||||||
|
|
||||||
|
if not _confirm_remove(args, out):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain_bare
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["CHATMAIL_INI"] = str(args.inipath)
|
||||||
|
env["CHATMAIL_KEEP_PACKAGES"] = "True" if args.keep_packages else ""
|
||||||
|
remove_path = importlib.resources.files(__package__).joinpath("remove.py").resolve()
|
||||||
|
pyinf = "pyinfra --dry" if args.dry_run else "pyinfra"
|
||||||
|
|
||||||
|
cmd = f"{pyinf} --ssh-user root {ssh_host} {remove_path} -y"
|
||||||
|
if ssh_host == "localhost":
|
||||||
|
cmd = f"{pyinf} @local {remove_path} -y"
|
||||||
|
|
||||||
|
if version.parse(pyinfra.__version__) < version.parse("3"):
|
||||||
|
out.red("Please re-run scripts/initenv.sh to update pyinfra to version 3.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
out.check_call(cmd, env=env)
|
||||||
|
if args.dry_run:
|
||||||
|
out.green("Remove dry run completed.")
|
||||||
|
else:
|
||||||
|
out.green("Remove completed.")
|
||||||
|
return 0
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
out.red("Remove failed")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def dns_cmd_options(parser):
|
def dns_cmd_options(parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--zonefile",
|
"--zonefile",
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import importlib.resources
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pyinfra
|
||||||
|
|
||||||
|
# pyinfra runs this module as a python file and not as a module so
|
||||||
|
# import paths must be absolute
|
||||||
|
from cmdeploy.removers import remove_chatmail
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config_path = os.getenv(
|
||||||
|
"CHATMAIL_INI",
|
||||||
|
importlib.resources.files("cmdeploy").joinpath("../../../chatmail.ini"),
|
||||||
|
)
|
||||||
|
keep_packages = bool(os.environ.get("CHATMAIL_KEEP_PACKAGES"))
|
||||||
|
|
||||||
|
remove_chatmail(config_path, keep_packages=keep_packages)
|
||||||
|
|
||||||
|
|
||||||
|
if pyinfra.is_cli:
|
||||||
|
main()
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
import shlex
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from chatmaild.config import read_config
|
||||||
|
from pyinfra.operations import apt, files, server
|
||||||
|
|
||||||
|
CHATMAIL_UNITS = [
|
||||||
|
"doveauth.service",
|
||||||
|
"lastlogin.service",
|
||||||
|
"chatmail-metadata.service",
|
||||||
|
"chatmail-expire.service",
|
||||||
|
"chatmail-expire.timer",
|
||||||
|
"chatmail-fsreport.service",
|
||||||
|
"chatmail-fsreport.timer",
|
||||||
|
"filtermail.service",
|
||||||
|
"filtermail-incoming.service",
|
||||||
|
"filtermail-transport.service",
|
||||||
|
"turnserver.service",
|
||||||
|
"iroh-relay.service",
|
||||||
|
"mtail.service",
|
||||||
|
"acmetool-redirector.service",
|
||||||
|
"acmetool-reconcile.service",
|
||||||
|
"acmetool-reconcile.timer",
|
||||||
|
"tls-cert-reload.path",
|
||||||
|
"tls-cert-reload.service",
|
||||||
|
]
|
||||||
|
|
||||||
|
LEGACY_UNITS = [
|
||||||
|
"doveauth-dictproxy.service",
|
||||||
|
"echobot.service",
|
||||||
|
"mta-sts-daemon.service",
|
||||||
|
]
|
||||||
|
|
||||||
|
PACKAGE_UNITS = [
|
||||||
|
"dovecot.service",
|
||||||
|
"postfix.service",
|
||||||
|
"nginx.service",
|
||||||
|
"opendkim.service",
|
||||||
|
"unbound.service",
|
||||||
|
"fcgiwrap.service",
|
||||||
|
]
|
||||||
|
|
||||||
|
PACKAGE_NAMES = [
|
||||||
|
"acmetool",
|
||||||
|
"dovecot-core",
|
||||||
|
"dovecot-imapd",
|
||||||
|
"dovecot-lmtpd",
|
||||||
|
"postfix",
|
||||||
|
"opendkim",
|
||||||
|
"opendkim-tools",
|
||||||
|
"nginx",
|
||||||
|
"nginx-common",
|
||||||
|
"libnginx-mod-stream",
|
||||||
|
"fcgiwrap",
|
||||||
|
"unbound",
|
||||||
|
"unbound-anchor",
|
||||||
|
"dnsutils",
|
||||||
|
"python3-virtualenv",
|
||||||
|
"gcc",
|
||||||
|
"python3-dev",
|
||||||
|
"curl",
|
||||||
|
"rsync",
|
||||||
|
]
|
||||||
|
|
||||||
|
RELAY_FILES = [
|
||||||
|
"/etc/apt/apt.conf.d/00InstallRecommends",
|
||||||
|
"/etc/apt/keyrings/obs-home-deltachat.gpg",
|
||||||
|
"/etc/apt/preferences.d/pin-dovecot",
|
||||||
|
"/etc/chatmail-nocreate",
|
||||||
|
"/etc/chatmail-version",
|
||||||
|
"/etc/cron.d/acmetool",
|
||||||
|
"/etc/cron.d/chatmail-metrics",
|
||||||
|
"/etc/cron.d/expunge",
|
||||||
|
"/etc/dovecot/auth.conf",
|
||||||
|
"/etc/dovecot/dovecot.conf",
|
||||||
|
"/etc/dovecot/push_notification.lua",
|
||||||
|
"/etc/iroh-relay.toml",
|
||||||
|
"/etc/mailname",
|
||||||
|
"/etc/mta-sts-daemon.yml",
|
||||||
|
"/etc/mtail/delivered_mail.mtail",
|
||||||
|
"/etc/nginx/nginx.conf",
|
||||||
|
"/etc/opendkim.conf",
|
||||||
|
"/etc/postfix/lmtp_header_cleanup",
|
||||||
|
"/etc/postfix/login_map",
|
||||||
|
"/etc/postfix/main.cf",
|
||||||
|
"/etc/postfix/master.cf",
|
||||||
|
"/etc/postfix/smtp_tls_policy_map",
|
||||||
|
"/etc/postfix/smtp_tls_policy_map.db",
|
||||||
|
"/etc/postfix/submission_header_cleanup",
|
||||||
|
"/etc/systemd/journald.conf",
|
||||||
|
"/etc/systemd/system/mta-sts-daemon.service",
|
||||||
|
"/etc/systemd/system/dovecot.service.d/10_restart.conf",
|
||||||
|
"/etc/systemd/system/opendkim.service.d/10-prevent-memory-leak.conf",
|
||||||
|
"/etc/systemd/system/postfix@.service.d/10_restart.conf",
|
||||||
|
"/etc/unbound/unbound.conf.d/chatmail.conf",
|
||||||
|
"/usr/lib/cgi-bin/newemail.py",
|
||||||
|
"/usr/local/bin/chatmail-turn",
|
||||||
|
"/usr/local/bin/filtermail",
|
||||||
|
"/usr/local/bin/iroh-relay",
|
||||||
|
"/usr/local/bin/mtail",
|
||||||
|
"/usr/sbin/policy-rc.d",
|
||||||
|
"/var/www/html/metrics",
|
||||||
|
]
|
||||||
|
|
||||||
|
PACKAGE_CONFIG_DIRS = [
|
||||||
|
"/etc/dkimkeys",
|
||||||
|
"/etc/dovecot",
|
||||||
|
"/etc/nginx",
|
||||||
|
"/etc/opendkim",
|
||||||
|
"/etc/postfix",
|
||||||
|
"/etc/unbound",
|
||||||
|
]
|
||||||
|
|
||||||
|
RELAY_DIRS = [
|
||||||
|
"/etc/mtail",
|
||||||
|
"/root/from-cmdeploy",
|
||||||
|
"/usr/local/lib/chatmaild",
|
||||||
|
"/usr/local/lib/postfix-mta-sts-resolver",
|
||||||
|
"/var/lib/dovecot",
|
||||||
|
"/var/lib/nginx",
|
||||||
|
"/var/lib/postfix",
|
||||||
|
"/var/lib/unbound",
|
||||||
|
"/var/log/nginx",
|
||||||
|
"/var/spool/postfix",
|
||||||
|
"/var/www/html",
|
||||||
|
]
|
||||||
|
|
||||||
|
EMPTY_SYSTEMD_DIRS = [
|
||||||
|
"/etc/systemd/system/dovecot.service.d",
|
||||||
|
"/etc/systemd/system/opendkim.service.d",
|
||||||
|
"/etc/systemd/system/postfix@.service.d",
|
||||||
|
]
|
||||||
|
|
||||||
|
RELAY_USERS = ["vmail", "iroh", "opendkim"]
|
||||||
|
RELAY_GROUPS = ["vmail", "iroh", "opendkim"]
|
||||||
|
|
||||||
|
|
||||||
|
def _quote(value) -> str:
|
||||||
|
return shlex.quote(str(value))
|
||||||
|
|
||||||
|
|
||||||
|
def _systemd_command(args: str) -> str:
|
||||||
|
return (
|
||||||
|
"if command -v systemctl >/dev/null && [ -d /run/systemd/system ]; "
|
||||||
|
f"then systemctl {args} || true; fi"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_file(path: str):
|
||||||
|
files.file(name=f"Remove {path}", path=path, present=False)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_dir(path: str):
|
||||||
|
files.directory(name=f"Remove {path}", path=path, present=False)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_units():
|
||||||
|
units = CHATMAIL_UNITS + LEGACY_UNITS + PACKAGE_UNITS
|
||||||
|
quoted_units = " ".join(_quote(unit) for unit in units)
|
||||||
|
server.shell(
|
||||||
|
name="Stop and disable chatmail services",
|
||||||
|
commands=[_systemd_command(f"disable --now {quoted_units}")],
|
||||||
|
)
|
||||||
|
|
||||||
|
for unit in CHATMAIL_UNITS + LEGACY_UNITS:
|
||||||
|
_remove_file(f"/etc/systemd/system/{unit}")
|
||||||
|
for path in RELAY_FILES:
|
||||||
|
_remove_file(path)
|
||||||
|
|
||||||
|
dirs = " ".join(_quote(path) for path in EMPTY_SYSTEMD_DIRS)
|
||||||
|
server.shell(
|
||||||
|
name="Remove empty chatmail systemd drop-in directories",
|
||||||
|
commands=[f"rmdir --ignore-fail-on-non-empty {dirs} 2>/dev/null || true"],
|
||||||
|
)
|
||||||
|
server.shell(
|
||||||
|
name="Reload systemd after removing chatmail units",
|
||||||
|
commands=[
|
||||||
|
_systemd_command("daemon-reload"),
|
||||||
|
_systemd_command("reset-failed"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_packages(keep_packages: bool):
|
||||||
|
if keep_packages:
|
||||||
|
return
|
||||||
|
apt.packages(
|
||||||
|
name="Purge chatmail relay packages",
|
||||||
|
packages=PACKAGE_NAMES,
|
||||||
|
present=False,
|
||||||
|
purge=True,
|
||||||
|
)
|
||||||
|
server.shell(
|
||||||
|
name="Remove downloaded dovecot packages",
|
||||||
|
commands=[
|
||||||
|
"rm -f -- /root/dovecot-core_*.deb "
|
||||||
|
"/root/dovecot-imapd_*.deb /root/dovecot-lmtpd_*.deb"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_dynamic_state(config, keep_packages: bool):
|
||||||
|
if not keep_packages:
|
||||||
|
for path in PACKAGE_CONFIG_DIRS:
|
||||||
|
_remove_dir(path)
|
||||||
|
|
||||||
|
for path in RELAY_DIRS:
|
||||||
|
_remove_dir(path)
|
||||||
|
|
||||||
|
_remove_dir(str(config.mailboxes_dir))
|
||||||
|
|
||||||
|
passdb = _quote(config.passdb_path)
|
||||||
|
server.shell(
|
||||||
|
name="Remove legacy chatmail passdb files",
|
||||||
|
commands=[f"rm -f -- {passdb} {passdb}.old {passdb}-*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
home_vmail = Path("/home/vmail")
|
||||||
|
if home_vmail == config.mailboxes_dir or home_vmail in config.mailboxes_dir.parents:
|
||||||
|
_remove_dir(str(home_vmail))
|
||||||
|
|
||||||
|
if config.tls_cert_mode == "self":
|
||||||
|
_remove_file("/etc/ssl/certs/mailserver.pem")
|
||||||
|
_remove_file("/etc/ssl/private/mailserver.key")
|
||||||
|
elif config.tls_cert_mode == "acme":
|
||||||
|
_remove_dir("/etc/acme")
|
||||||
|
_remove_dir("/var/lib/acme")
|
||||||
|
|
||||||
|
|
||||||
|
def _restore_basic_resolver():
|
||||||
|
server.shell(
|
||||||
|
name="Restore basic resolver after removing unbound",
|
||||||
|
commands=[
|
||||||
|
_systemd_command("unmask systemd-resolved.service"),
|
||||||
|
_systemd_command("enable --now systemd-resolved.service"),
|
||||||
|
"if [ -e /run/systemd/resolve/stub-resolv.conf ]; then "
|
||||||
|
"ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf; "
|
||||||
|
"else printf 'nameserver 9.9.9.9\\n' >/etc/resolv.conf; fi",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_environment_changes():
|
||||||
|
files.line(
|
||||||
|
name="Remove chatmail TZ environment override",
|
||||||
|
path="/etc/environment",
|
||||||
|
line="TZ=:/etc/localtime",
|
||||||
|
present=False,
|
||||||
|
)
|
||||||
|
files.line(
|
||||||
|
name="Remove chatmail inotify instance sysctl override",
|
||||||
|
path="/etc/sysctl.conf",
|
||||||
|
line="fs.inotify.max_user_instances = 65535",
|
||||||
|
present=False,
|
||||||
|
)
|
||||||
|
files.line(
|
||||||
|
name="Remove chatmail inotify watches sysctl override",
|
||||||
|
path="/etc/sysctl.conf",
|
||||||
|
line="fs.inotify.max_user_watches = 65535",
|
||||||
|
present=False,
|
||||||
|
)
|
||||||
|
files.line(
|
||||||
|
name="Remove legacy chatmail dovecot package repository",
|
||||||
|
path="/etc/apt/sources.list",
|
||||||
|
line="deb [signed-by=/etc/apt/keyrings/obs-home-deltachat.gpg] https://download.opensuse.org/repositories/home:/deltachat/Debian_12/ ./",
|
||||||
|
escape_regex_characters=True,
|
||||||
|
present=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_users():
|
||||||
|
commands = []
|
||||||
|
for user in RELAY_USERS:
|
||||||
|
quoted = _quote(user)
|
||||||
|
commands.append(f"userdel -r {quoted} 2>/dev/null || userdel {quoted} || true")
|
||||||
|
for group in RELAY_GROUPS:
|
||||||
|
commands.append(f"groupdel {_quote(group)} 2>/dev/null || true")
|
||||||
|
server.shell(name="Remove chatmail users and groups", commands=commands)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_chatmail(config_path: Path, keep_packages: bool = False) -> None:
|
||||||
|
"""Remove the deployed chatmail relay from the target host."""
|
||||||
|
config = read_config(config_path)
|
||||||
|
|
||||||
|
_remove_units()
|
||||||
|
_remove_packages(keep_packages=keep_packages)
|
||||||
|
_remove_dynamic_state(config, keep_packages=keep_packages)
|
||||||
|
_remove_environment_changes()
|
||||||
|
_restore_basic_resolver()
|
||||||
|
_remove_users()
|
||||||
@@ -47,6 +47,50 @@ class TestCmdline:
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert out == "[WARNING] 1.3.3.7 is not a domain, skipping DNS checks.\n"
|
assert out == "[WARNING] 1.3.3.7 is not a domain, skipping DNS checks.\n"
|
||||||
|
|
||||||
|
def test_remove_cancelled_on_wrong_confirmation(self, tmp_path, monkeypatch):
|
||||||
|
monkeypatch.delenv("CHATMAIL_INI", raising=False)
|
||||||
|
inipath = tmp_path / "chatmail.ini"
|
||||||
|
assert main(["init", "--config", str(inipath), "chat.example.org"]) == 0
|
||||||
|
|
||||||
|
def check_call(self, arg, env=None, quiet=False):
|
||||||
|
raise AssertionError("remove command should not run after cancellation")
|
||||||
|
|
||||||
|
monkeypatch.setattr("cmdeploy.cmdeploy.Out.check_call", check_call)
|
||||||
|
monkeypatch.setattr("builtins.input", lambda prompt: "wrong.example.org")
|
||||||
|
assert main(["remove", "--config", str(inipath)]) == 1
|
||||||
|
|
||||||
|
def test_remove_dry_run_local(self, tmp_path, monkeypatch):
|
||||||
|
monkeypatch.delenv("CHATMAIL_INI", raising=False)
|
||||||
|
inipath = tmp_path / "chatmail.ini"
|
||||||
|
assert main(["init", "--config", str(inipath), "chat.example.org"]) == 0
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def check_call(self, arg, env=None, quiet=False):
|
||||||
|
calls.append((arg, env))
|
||||||
|
|
||||||
|
monkeypatch.setattr("cmdeploy.cmdeploy.Out.check_call", check_call)
|
||||||
|
assert (
|
||||||
|
main(
|
||||||
|
[
|
||||||
|
"remove",
|
||||||
|
"--config",
|
||||||
|
str(inipath),
|
||||||
|
"--ssh-host",
|
||||||
|
"localhost",
|
||||||
|
"--dry-run",
|
||||||
|
"--yes",
|
||||||
|
"--keep-packages",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
== 0
|
||||||
|
)
|
||||||
|
cmd, env = calls[0]
|
||||||
|
assert cmd.startswith("pyinfra --dry @local ")
|
||||||
|
assert "remove.py -y" in cmd
|
||||||
|
assert env["CHATMAIL_INI"] == str(inipath)
|
||||||
|
assert env["CHATMAIL_KEEP_PACKAGES"] == "True"
|
||||||
|
|
||||||
|
|
||||||
def test_www_folder(example_config, tmp_path):
|
def test_www_folder(example_config, tmp_path):
|
||||||
reporoot = importlib.resources.files(__package__).joinpath("../../../../").resolve()
|
reporoot = importlib.resources.files(__package__).joinpath("../../../../").resolve()
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
from unittest.mock import call
|
||||||
|
|
||||||
|
from cmdeploy import removers
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_chatmail_purges_packages_and_state(make_config, monkeypatch):
|
||||||
|
config = make_config("chat.example.org")
|
||||||
|
|
||||||
|
apt_calls = []
|
||||||
|
file_calls = []
|
||||||
|
dir_calls = []
|
||||||
|
shell_calls = []
|
||||||
|
line_calls = []
|
||||||
|
|
||||||
|
monkeypatch.setattr(removers.apt, "packages", lambda **kw: apt_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.files, "file", lambda **kw: file_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.files, "directory", lambda **kw: dir_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.server, "shell", lambda **kw: shell_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.files, "line", lambda **kw: line_calls.append(kw))
|
||||||
|
|
||||||
|
removers.remove_chatmail(config._inipath)
|
||||||
|
|
||||||
|
assert apt_calls == [
|
||||||
|
{
|
||||||
|
"name": "Purge chatmail relay packages",
|
||||||
|
"packages": removers.PACKAGE_NAMES,
|
||||||
|
"present": False,
|
||||||
|
"purge": True,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
assert call(path="/usr/local/lib/chatmaild", present=False) in [
|
||||||
|
call(path=entry["path"], present=entry["present"]) for entry in dir_calls
|
||||||
|
]
|
||||||
|
assert call(path=str(config.mailboxes_dir), present=False) in [
|
||||||
|
call(path=entry["path"], present=entry["present"]) for entry in dir_calls
|
||||||
|
]
|
||||||
|
assert any(entry["path"] == "/var/lib/acme" for entry in dir_calls)
|
||||||
|
assert any(
|
||||||
|
entry["path"] == "/etc/systemd/system/doveauth.service" for entry in file_calls
|
||||||
|
)
|
||||||
|
assert any(entry["path"] == "/etc/postfix/main.cf" for entry in file_calls)
|
||||||
|
assert any(entry["path"] == "/etc/opendkim.conf" for entry in file_calls)
|
||||||
|
assert any(entry["path"] == "/etc/postfix" for entry in dir_calls)
|
||||||
|
assert any(entry["path"] == "/var/log/nginx" for entry in dir_calls)
|
||||||
|
assert any(
|
||||||
|
"userdel -r vmail" in command
|
||||||
|
for entry in shell_calls
|
||||||
|
for command in entry["commands"]
|
||||||
|
)
|
||||||
|
assert any(entry["path"] == "/etc/environment" for entry in line_calls)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_chatmail_keep_packages_and_external_tls(make_config, monkeypatch):
|
||||||
|
config = make_config(
|
||||||
|
"chat.example.org",
|
||||||
|
{"tls_external_cert_and_key": "/certs/fullchain.pem /certs/privkey.pem"},
|
||||||
|
)
|
||||||
|
|
||||||
|
apt_calls = []
|
||||||
|
file_calls = []
|
||||||
|
dir_calls = []
|
||||||
|
|
||||||
|
monkeypatch.setattr(removers.apt, "packages", lambda **kw: apt_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.files, "file", lambda **kw: file_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.files, "directory", lambda **kw: dir_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.server, "shell", lambda **kw: None)
|
||||||
|
monkeypatch.setattr(removers.files, "line", lambda **kw: None)
|
||||||
|
|
||||||
|
removers.remove_chatmail(config._inipath, keep_packages=True)
|
||||||
|
|
||||||
|
assert apt_calls == []
|
||||||
|
removed_files = {entry["path"] for entry in file_calls}
|
||||||
|
removed_dirs = {entry["path"] for entry in dir_calls}
|
||||||
|
assert "/certs/fullchain.pem" not in removed_files
|
||||||
|
assert "/certs/privkey.pem" not in removed_files
|
||||||
|
assert "/var/lib/acme" not in removed_dirs
|
||||||
|
assert "/etc/nginx" not in removed_dirs
|
||||||
|
assert "/etc/unbound" not in removed_dirs
|
||||||
|
assert "/etc/postfix" not in removed_dirs
|
||||||
|
assert "/etc/dovecot" not in removed_dirs
|
||||||
|
assert "/etc/nginx/nginx.conf" in removed_files
|
||||||
|
assert "/etc/unbound/unbound.conf.d/chatmail.conf" in removed_files
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_chatmail_removes_self_signed_tls(make_config, monkeypatch):
|
||||||
|
config = make_config("_test.example.org")
|
||||||
|
file_calls = []
|
||||||
|
|
||||||
|
monkeypatch.setattr(removers.apt, "packages", lambda **kw: None)
|
||||||
|
monkeypatch.setattr(removers.files, "file", lambda **kw: file_calls.append(kw))
|
||||||
|
monkeypatch.setattr(removers.files, "directory", lambda **kw: None)
|
||||||
|
monkeypatch.setattr(removers.server, "shell", lambda **kw: None)
|
||||||
|
monkeypatch.setattr(removers.files, "line", lambda **kw: None)
|
||||||
|
|
||||||
|
removers.remove_chatmail(config._inipath)
|
||||||
|
|
||||||
|
removed = {entry["path"] for entry in file_calls}
|
||||||
|
assert "/etc/ssl/certs/mailserver.pem" in removed
|
||||||
|
assert "/etc/ssl/private/mailserver.key" in removed
|
||||||
Reference in New Issue
Block a user