Compare commits

..

1 Commits

Author SHA1 Message Date
link2xt
2b806971a3 Avoid datetime overflow, cache deb for a year instead of 300k years 2025-07-08 17:53:29 +00:00
7 changed files with 30 additions and 88 deletions

View File

@@ -2,9 +2,6 @@
## untagged
- Check whether GCC is installed in initenv.sh
([#608](https://github.com/chatmail/relay/pull/608))
- Expire push notification tokens after 90 days
([#583](https://github.com/chatmail/relay/pull/583))
@@ -17,9 +14,6 @@
- Reconfigure Dovecot imap-login service to high-performance mode
([#578](https://github.com/chatmail/relay/pull/578))
- Set timezone to improve dovecot performance
([#584](https://github.com/chatmail/relay/pull/584))
- Increase nginx connection limits
([#576](https://github.com/chatmail/relay/pull/576))

View File

@@ -541,6 +541,3 @@ Here are some related projects that you may be interested in:
progress](https://github.com/mjl-/mox/issues/251) to modify it to support all
of the features and configuration settings required to operate as a chatmail
relay.
- [Maddy-Chatmail](https://github.com/sadraiiali/maddy_chatmail): a plugin for the
[Maddy email server](https://maddy.email/) which aims to implement the
chatmail relay features and configuration options.

View File

@@ -346,7 +346,7 @@ def _install_dovecot_package(package: str, arch: str):
src=url,
dest=deb_filename,
sha256sum=sha256,
cache_time=60 * 60 * 24 * 365 * 10, # never redownload the package
cache_time=60 * 60 * 24 * 365, # cache the .deb for a year,
)
apt.deb(name=f"Install dovecot-{package}", src=deb_filename)
@@ -410,13 +410,6 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool:
persist=True,
)
timezone_env = files.line(
name="Set TZ environment variable",
path="/etc/environment",
line="TZ=:/etc/localtime",
)
need_restart |= timezone_env.changed
return need_restart
@@ -716,10 +709,9 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
packages="postfix",
)
if not "dovecot.service" in host.get_fact(SystemdEnabled):
_install_dovecot_package("core", host.get_fact(facts.server.Arch))
_install_dovecot_package("imapd", host.get_fact(facts.server.Arch))
_install_dovecot_package("lmtpd", host.get_fact(facts.server.Arch))
_install_dovecot_package("core", host.get_fact(facts.server.Arch))
_install_dovecot_package("imapd", host.get_fact(facts.server.Arch))
_install_dovecot_package("lmtpd", host.get_fact(facts.server.Arch))
apt.packages(
name="Install nginx",
@@ -816,14 +808,8 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
name="Ensure cron is installed",
packages=["cron"],
)
try:
git_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode()
except Exception:
git_hash = "unknown\n"
try:
git_diff = subprocess.check_output(["git", "diff"]).decode()
except Exception:
git_diff = ""
git_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode()
git_diff = subprocess.check_output(["git", "diff"]).decode()
files.put(
name="Upload chatmail relay git commiit hash",
src=StringIO(git_hash + git_diff),

View File

@@ -19,7 +19,7 @@ from packaging import version
from termcolor import colored
from . import dns, remote
from .sshexec import SSHExec, Local
from .sshexec import SSHExec
#
# cmdeploy sub commands and options
@@ -62,18 +62,13 @@ def run_cmd_options(parser):
"--ssh-host",
dest="ssh_host",
help="specify an SSH host to deploy to; uses mail_domain from chatmail.ini by default",
default=None,
)
def run_cmd(args, out):
"""Deploy chatmail services on the remote server."""
ssh_host = args.ssh_host if args.ssh_host else args.config.mail_domain
if ssh_host == "localhost":
sshexec = Local(ssh_host)
else:
sshexec = args.get_sshexec(ssh_host)
sshexec = args.get_sshexec()
require_iroh = args.config.enable_iroh_relay
remote_data = dns.get_initial_remote_data(sshexec, args.config.mail_domain)
if not dns.check_initial_remote_data(remote_data, print=out.red):
@@ -85,7 +80,7 @@ def run_cmd(args, out):
env["CHATMAIL_REQUIRE_IROH"] = "True" if require_iroh else ""
deploy_path = importlib.resources.files(__package__).joinpath("deploy.py").resolve()
pyinf = "pyinfra --dry" if args.dry_run else "pyinfra"
ssh_host = "@local" if ssh_host == "localhost" else f"--ssh-host {ssh_host}"
ssh_host = args.config.mail_domain if not args.ssh_host else args.ssh_host
cmd = f"{pyinf} --ssh-user root {ssh_host} {deploy_path} -y"
if version.parse(pyinfra.__version__) < version.parse("3"):
out.red("Please re-run scripts/initenv.sh to update pyinfra to version 3.")
@@ -335,9 +330,9 @@ def main(args=None):
if not hasattr(args, "func"):
return parser.parse_args(["-h"])
def get_sshexec(host):
print(f"[ssh] login to {host}")
return SSHExec(host, verbose=args.verbose)
def get_sshexec():
print(f"[ssh] login to {args.config.mail_domain}")
return SSHExec(args.config.mail_domain, verbose=args.verbose)
args.get_sshexec = get_sshexec

View File

@@ -1,6 +1,5 @@
import inspect
import os
import subprocess
import sys
from queue import Queue
@@ -45,16 +44,30 @@ def print_stderr(item="", end="\n"):
print(item, file=sys.stderr, end=end)
class Exec:
class SSHExec:
RemoteError = execnet.RemoteError
FuncError = FuncError
def __init__(self, host, verbose, timeout):
self.host = host
def __init__(self, host, verbose=False, python="python3", timeout=60):
self.gateway = execnet.makegateway(f"ssh=root@{host}//python={python}")
self._remote_cmdloop_channel = bootstrap_remote(self.gateway, remote)
self.timeout = timeout
self.verbose = verbose
def __call__(self, call, kwargs=None, log_callback=None):
return subprocess.check_output(call)
if kwargs is None:
kwargs = {}
assert call.__module__.startswith("cmdeploy.remote")
modname = call.__module__.replace("cmdeploy.", "")
self._remote_cmdloop_channel.send((modname, call.__name__, kwargs))
while 1:
code, data = self._remote_cmdloop_channel.receive(timeout=self.timeout)
if log_callback is not None and code == "log":
log_callback(data)
elif code == "finish":
return data
elif code == "error":
raise self.FuncError(data)
def logged(self, call, kwargs):
def log_progress(data):
@@ -72,33 +85,3 @@ class Exec:
res = self(call, kwargs, log_callback=log_progress)
print_stderr()
return res
class Local(Exec):
def __init__(self, host, verbose=False, timeout=60):
super().__init__(host, verbose, timeout)
class SSHExec(Exec):
RemoteError = execnet.RemoteError
def __init__(self, host, verbose=False, timeout=60):
super().__init__(host, verbose, timeout)
self.gateway = execnet.makegateway(f"ssh=root@{host}//python=python3")
self._remote_cmdloop_channel = bootstrap_remote(self.gateway, remote)
def __call__(self, call, kwargs=None, log_callback=None):
if kwargs is None:
kwargs = {}
assert call.__module__.startswith("cmdeploy.remote")
modname = call.__module__.replace("cmdeploy.", "")
self._remote_cmdloop_channel.send((modname, call.__name__, kwargs))
while 1:
code, data = self._remote_cmdloop_channel.receive(timeout=self.timeout)
if log_callback is not None and code == "log":
log_callback(data)
elif code == "finish":
return data
elif code == "error":
raise self.FuncError(data)

View File

@@ -65,14 +65,6 @@ class TestSSHExecutor:
assert (now - since_date).total_seconds() < 60 * 60 * 51
def test_timezone_env(remote):
for line in remote.iter_output("env"):
print(line)
if line == "tz=:/etc/localtime":
return True
pytest.fail("TZ is not set")
def test_remote(remote, imap_or_smtp):
lineproducer = remote.iter_output(imap_or_smtp.logcmd)
imap_or_smtp.connect()

View File

@@ -9,11 +9,6 @@ if command -v lsb_release 2>&1 >/dev/null; then
echo "You need to install python3-dev for installing the other dependencies."
exit 1
fi
if ! gcc --version 2>&1 >/dev/null
then
echo "You need to install gcc for building Python dependencies."
exit 1
fi
;;
esac
fi