mirror of
https://github.com/chatmail/relay.git
synced 2026-05-19 12:28:06 +00:00
apply results of "cmdeploy fmt"
This commit is contained in:
@@ -27,7 +27,9 @@ class AcmetoolDeployer(Deployer):
|
|||||||
|
|
||||||
files.put(
|
files.put(
|
||||||
name="Install acmetool hook.",
|
name="Install acmetool hook.",
|
||||||
src=importlib.resources.files(__package__).joinpath("acmetool.hook").open("rb"),
|
src=importlib.resources.files(__package__)
|
||||||
|
.joinpath("acmetool.hook")
|
||||||
|
.open("rb"),
|
||||||
dest="/etc/acme/hooks/nginx",
|
dest="/etc/acme/hooks/nginx",
|
||||||
user="root",
|
user="root",
|
||||||
group="root",
|
group="root",
|
||||||
@@ -41,7 +43,9 @@ class AcmetoolDeployer(Deployer):
|
|||||||
|
|
||||||
def configure(self):
|
def configure(self):
|
||||||
files.template(
|
files.template(
|
||||||
src=importlib.resources.files(__package__).joinpath("response-file.yaml.j2"),
|
src=importlib.resources.files(__package__).joinpath(
|
||||||
|
"response-file.yaml.j2"
|
||||||
|
),
|
||||||
dest="/var/lib/acme/conf/responses",
|
dest="/var/lib/acme/conf/responses",
|
||||||
user="root",
|
user="root",
|
||||||
group="root",
|
group="root",
|
||||||
@@ -80,7 +84,9 @@ class AcmetoolDeployer(Deployer):
|
|||||||
self.need_restart_reconcile_service = reconcile_service_file.changed
|
self.need_restart_reconcile_service = reconcile_service_file.changed
|
||||||
|
|
||||||
reconcile_timer_file = files.put(
|
reconcile_timer_file = files.put(
|
||||||
src=importlib.resources.files(__package__).joinpath("acmetool-reconcile.timer"),
|
src=importlib.resources.files(__package__).joinpath(
|
||||||
|
"acmetool-reconcile.timer"
|
||||||
|
),
|
||||||
dest="/etc/systemd/system/acmetool-reconcile.timer",
|
dest="/etc/systemd/system/acmetool-reconcile.timer",
|
||||||
user="root",
|
user="root",
|
||||||
group="root",
|
group="root",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from packaging import version
|
|||||||
from termcolor import colored
|
from termcolor import colored
|
||||||
|
|
||||||
from . import dns, remote
|
from . import dns, remote
|
||||||
from .sshexec import SSHExec, LocalExec
|
from .sshexec import LocalExec, SSHExec
|
||||||
|
|
||||||
#
|
#
|
||||||
# cmdeploy sub commands and options
|
# cmdeploy sub commands and options
|
||||||
@@ -309,7 +309,7 @@ def add_ssh_host_option(parser):
|
|||||||
"--ssh-host",
|
"--ssh-host",
|
||||||
dest="ssh_host",
|
dest="ssh_host",
|
||||||
help="Run commands on 'localhost', via '@docker', or on a specific SSH host "
|
help="Run commands on 'localhost', via '@docker', or on a specific SSH host "
|
||||||
"instead of chatmail.ini's mail_domain.",
|
"instead of chatmail.ini's mail_domain.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from io import StringIO
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from chatmaild.config import Config, read_config
|
from chatmaild.config import Config, read_config
|
||||||
from cmdeploy.cmdeploy import Out
|
|
||||||
from pyinfra import facts, host, logger
|
from pyinfra import facts, host, logger
|
||||||
from pyinfra.api import FactBase
|
from pyinfra.api import FactBase
|
||||||
from pyinfra.facts.files import File, Sha256File
|
from pyinfra.facts.files import File, Sha256File
|
||||||
@@ -19,6 +18,8 @@ from pyinfra.facts.server import Sysctl
|
|||||||
from pyinfra.facts.systemd import SystemdEnabled
|
from pyinfra.facts.systemd import SystemdEnabled
|
||||||
from pyinfra.operations import apt, files, pip, server, systemd
|
from pyinfra.operations import apt, files, pip, server, systemd
|
||||||
|
|
||||||
|
from cmdeploy.cmdeploy import Out
|
||||||
|
|
||||||
from .acmetool import AcmetoolDeployer
|
from .acmetool import AcmetoolDeployer
|
||||||
from .basedeploy import Deployer, Deployment
|
from .basedeploy import Deployer, Deployment
|
||||||
from .www import build_webpages, find_merge_conflict, get_paths
|
from .www import build_webpages, find_merge_conflict, get_paths
|
||||||
@@ -148,7 +149,9 @@ def _configure_remote_units(mail_domain, units) -> None:
|
|||||||
|
|
||||||
basename = fn if "." in fn else f"{fn}.service"
|
basename = fn if "." in fn else f"{fn}.service"
|
||||||
|
|
||||||
source_path = importlib.resources.files(__package__).joinpath("service", f"{basename}.f")
|
source_path = importlib.resources.files(__package__).joinpath(
|
||||||
|
"service", f"{basename}.f"
|
||||||
|
)
|
||||||
content = source_path.read_text().format(**params).encode()
|
content = source_path.read_text().format(**params).encode()
|
||||||
|
|
||||||
files.put(
|
files.put(
|
||||||
@@ -179,7 +182,6 @@ def _activate_remote_units(units) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
def _configure_opendkim(domain: str, dkim_selector: str = "dkim") -> bool:
|
||||||
"""Configures OpenDKIM"""
|
"""Configures OpenDKIM"""
|
||||||
need_restart = False
|
need_restart = False
|
||||||
@@ -437,7 +439,9 @@ class PostfixDeployer(Deployer):
|
|||||||
restart = False if self.disable_mail else self.need_restart
|
restart = False if self.disable_mail else self.need_restart
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
name="disable postfix for now" if self.disable_mail else "Start and enable Postfix",
|
name="disable postfix for now"
|
||||||
|
if self.disable_mail
|
||||||
|
else "Start and enable Postfix",
|
||||||
service="postfix.service",
|
service="postfix.service",
|
||||||
running=False if self.disable_mail else True,
|
running=False if self.disable_mail else True,
|
||||||
enabled=False if self.disable_mail else True,
|
enabled=False if self.disable_mail else True,
|
||||||
@@ -569,7 +573,9 @@ class DovecotDeployer(Deployer):
|
|||||||
restart = False if self.disable_mail else self.need_restart
|
restart = False if self.disable_mail else self.need_restart
|
||||||
|
|
||||||
systemd.service(
|
systemd.service(
|
||||||
name="disable dovecot for now" if self.disable_mail else "Start and enable Dovecot",
|
name="disable dovecot for now"
|
||||||
|
if self.disable_mail
|
||||||
|
else "Start and enable Dovecot",
|
||||||
service="dovecot.service",
|
service="dovecot.service",
|
||||||
running=False if self.disable_mail else True,
|
running=False if self.disable_mail else True,
|
||||||
enabled=False if self.disable_mail else True,
|
enabled=False if self.disable_mail else True,
|
||||||
@@ -708,7 +714,9 @@ class WebsiteDeployer(Deployer):
|
|||||||
if not www_path.is_dir():
|
if not www_path.is_dir():
|
||||||
logger.warning("Building web pages is disabled in chatmail.ini, skipping")
|
logger.warning("Building web pages is disabled in chatmail.ini, skipping")
|
||||||
elif (path := find_merge_conflict(src_dir)) is not None:
|
elif (path := find_merge_conflict(src_dir)) is not None:
|
||||||
logger.warning(f"Merge conflict found in {path}, skipping website deployment. Fix merge conflict if you want to upload your web page.")
|
logger.warning(
|
||||||
|
f"Merge conflict found in {path}, skipping website deployment. Fix merge conflict if you want to upload your web page."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# if www_folder is a hugo page, build it
|
# if www_folder is a hugo page, build it
|
||||||
if build_dir:
|
if build_dir:
|
||||||
@@ -1106,12 +1114,10 @@ def deploy_chatmail(config_path: Path, disable_mail: bool) -> None:
|
|||||||
TurnDeployer(mail_domain),
|
TurnDeployer(mail_domain),
|
||||||
IrohDeployer(config.enable_iroh_relay),
|
IrohDeployer(config.enable_iroh_relay),
|
||||||
AcmetoolDeployer(config.acme_email, tls_domains),
|
AcmetoolDeployer(config.acme_email, tls_domains),
|
||||||
|
|
||||||
WebsiteDeployer(config),
|
WebsiteDeployer(config),
|
||||||
ChatmailVenvDeployer(config),
|
ChatmailVenvDeployer(config),
|
||||||
MtastsDeployer(),
|
MtastsDeployer(),
|
||||||
OpendkimDeployer(mail_domain),
|
OpendkimDeployer(mail_domain),
|
||||||
|
|
||||||
# Dovecot should be started before Postfix
|
# Dovecot should be started before Postfix
|
||||||
# because it creates authentication socket
|
# because it creates authentication socket
|
||||||
# required by Postfix.
|
# required by Postfix.
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ def check_full_zone(sshexec, remote_data, out, zonefile) -> int:
|
|||||||
and return (exitcode, remote_data) tuple."""
|
and return (exitcode, remote_data) tuple."""
|
||||||
|
|
||||||
required_diff, recommended_diff = sshexec.logged(
|
required_diff, recommended_diff = sshexec.logged(
|
||||||
remote.rdns.check_zonefile, kwargs=dict(zonefile=zonefile, verbose=False),
|
remote.rdns.check_zonefile,
|
||||||
|
kwargs=dict(zonefile=zonefile, verbose=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
returncode = 0
|
returncode = 0
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ All functions of this module
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .rshell import CalledProcessError, shell, log_progress
|
from .rshell import CalledProcessError, log_progress, shell
|
||||||
|
|
||||||
|
|
||||||
def perform_initial_checks(mail_domain, pre_command=""):
|
def perform_initial_checks(mail_domain, pre_command=""):
|
||||||
@@ -26,7 +26,9 @@ def perform_initial_checks(mail_domain, pre_command=""):
|
|||||||
WWW = query_dns("CNAME", f"www.{mail_domain}")
|
WWW = query_dns("CNAME", f"www.{mail_domain}")
|
||||||
|
|
||||||
res = dict(mail_domain=mail_domain, A=A, AAAA=AAAA, MTA_STS=MTA_STS, WWW=WWW)
|
res = dict(mail_domain=mail_domain, A=A, AAAA=AAAA, MTA_STS=MTA_STS, WWW=WWW)
|
||||||
res["acme_account_url"] = shell(pre_command + "acmetool account-url", fail_ok=True, print=log_progress)
|
res["acme_account_url"] = shell(
|
||||||
|
pre_command + "acmetool account-url", fail_ok=True, print=log_progress
|
||||||
|
)
|
||||||
res["dkim_entry"], res["web_dkim_entry"] = get_dkim_entry(
|
res["dkim_entry"], res["web_dkim_entry"] = get_dkim_entry(
|
||||||
mail_domain, pre_command, dkim_selector="opendkim"
|
mail_domain, pre_command, dkim_selector="opendkim"
|
||||||
)
|
)
|
||||||
@@ -45,7 +47,7 @@ def get_dkim_entry(mail_domain, pre_command, dkim_selector):
|
|||||||
dkim_pubkey = shell(
|
dkim_pubkey = shell(
|
||||||
f"{pre_command}openssl rsa -in /etc/dkimkeys/{dkim_selector}.private "
|
f"{pre_command}openssl rsa -in /etc/dkimkeys/{dkim_selector}.private "
|
||||||
"-pubout 2>/dev/null | awk '/-/{next}{printf(\"%s\",$0)}'",
|
"-pubout 2>/dev/null | awk '/-/{next}{printf(\"%s\",$0)}'",
|
||||||
print=log_progress
|
print=log_progress,
|
||||||
)
|
)
|
||||||
except CalledProcessError:
|
except CalledProcessError:
|
||||||
return
|
return
|
||||||
@@ -62,9 +64,9 @@ def query_dns(typ, domain):
|
|||||||
# Get autoritative nameserver from the SOA record.
|
# Get autoritative nameserver from the SOA record.
|
||||||
soa_answers = [
|
soa_answers = [
|
||||||
x.split()
|
x.split()
|
||||||
for x in shell(f"dig -r -q {domain} -t SOA +noall +authority +answer", print=log_progress).split(
|
for x in shell(
|
||||||
"\n"
|
f"dig -r -q {domain} -t SOA +noall +authority +answer", print=log_progress
|
||||||
)
|
).split("\n")
|
||||||
]
|
]
|
||||||
soa = [a for a in soa_answers if len(a) >= 3 and a[3] == "SOA"]
|
soa = [a for a in soa_answers if len(a) >= 3 and a[3] == "SOA"]
|
||||||
if not soa:
|
if not soa:
|
||||||
@@ -73,7 +75,7 @@ def query_dns(typ, domain):
|
|||||||
|
|
||||||
# Query authoritative nameserver directly to bypass DNS cache.
|
# Query authoritative nameserver directly to bypass DNS cache.
|
||||||
res = shell(f"dig @{ns} -r -q {domain} -t {typ} +short", print=log_progress)
|
res = shell(f"dig @{ns} -r -q {domain} -t {typ} +short", print=log_progress)
|
||||||
return next((line for line in res.split("\n") if not line.startswith(';')), '')
|
return next((line for line in res.split("\n") if not line.startswith(";")), "")
|
||||||
|
|
||||||
|
|
||||||
def check_zonefile(zonefile, verbose=True):
|
def check_zonefile(zonefile, verbose=True):
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from subprocess import DEVNULL, CalledProcessError, check_output
|
from subprocess import DEVNULL, CalledProcessError, check_output
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class LocalExec:
|
|||||||
where = "locally"
|
where = "locally"
|
||||||
if self.docker:
|
if self.docker:
|
||||||
if call == remote.rdns.perform_initial_checks:
|
if call == remote.rdns.perform_initial_checks:
|
||||||
kwargs['pre_command'] = "docker exec chatmail "
|
kwargs["pre_command"] = "docker exec chatmail "
|
||||||
where = "in docker"
|
where = "in docker"
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print(f"Running {where}: {call.__name__}(**{kwargs})")
|
print(f"Running {where}: {call.__name__}(**{kwargs})")
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class TestSSHExecutor:
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
assert err.startswith("Collecting")
|
assert err.startswith("Collecting")
|
||||||
# XXX could not figure out how capturing can be made to work properly
|
# XXX could not figure out how capturing can be made to work properly
|
||||||
#assert err.endswith("....\n")
|
# assert err.endswith("....\n")
|
||||||
assert err.count("\n") == 1
|
assert err.count("\n") == 1
|
||||||
|
|
||||||
sshexec.verbose = True
|
sshexec.verbose = True
|
||||||
@@ -45,7 +45,7 @@ class TestSSHExecutor:
|
|||||||
out, err = capsys.readouterr()
|
out, err = capsys.readouterr()
|
||||||
lines = err.split("\n")
|
lines = err.split("\n")
|
||||||
# XXX could not figure out how capturing can be made to work properly
|
# XXX could not figure out how capturing can be made to work properly
|
||||||
#assert len(lines) > 4
|
# assert len(lines) > 4
|
||||||
assert remote.rdns.perform_initial_checks.__doc__ in lines[0]
|
assert remote.rdns.perform_initial_checks.__doc__ in lines[0]
|
||||||
|
|
||||||
def test_exception(self, sshexec, capsys):
|
def test_exception(self, sshexec, capsys):
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ class TestPerformInitialChecks:
|
|||||||
remote_data = remote.rdns.perform_initial_checks("some.domain")
|
remote_data = remote.rdns.perform_initial_checks("some.domain")
|
||||||
assert remote_data["A"] == mockdns_expected["A"]["some.domain"]
|
assert remote_data["A"] == mockdns_expected["A"]["some.domain"]
|
||||||
assert remote_data["AAAA"] == mockdns_expected["AAAA"]["some.domain"]
|
assert remote_data["AAAA"] == mockdns_expected["AAAA"]["some.domain"]
|
||||||
assert remote_data["MTA_STS"] == mockdns_expected["CNAME"]["mta-sts.some.domain"]
|
assert (
|
||||||
|
remote_data["MTA_STS"] == mockdns_expected["CNAME"]["mta-sts.some.domain"]
|
||||||
|
)
|
||||||
assert remote_data["WWW"] == mockdns_expected["CNAME"]["www.some.domain"]
|
assert remote_data["WWW"] == mockdns_expected["CNAME"]["www.some.domain"]
|
||||||
|
|
||||||
@pytest.mark.parametrize("drop", ["A", "AAAA"])
|
@pytest.mark.parametrize("drop", ["A", "AAAA"])
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import importlib.resources
|
import importlib.resources
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
|
||||||
|
|
||||||
import markdown
|
import markdown
|
||||||
from chatmaild.config import read_config
|
from chatmaild.config import read_config
|
||||||
@@ -12,8 +12,9 @@ from jinja2 import Template
|
|||||||
|
|
||||||
from .genqr import gen_qr_png_data
|
from .genqr import gen_qr_png_data
|
||||||
|
|
||||||
|
_MERGE_CONFLICT_RE = re.compile(
|
||||||
_MERGE_CONFLICT_RE = re.compile(r"^<<<<<<<.+^=======.+^>>>>>>>", re.DOTALL | re.MULTILINE)
|
r"^<<<<<<<.+^=======.+^>>>>>>>", re.DOTALL | re.MULTILINE
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def snapshot_dir_stats(somedir):
|
def snapshot_dir_stats(somedir):
|
||||||
|
|||||||
Reference in New Issue
Block a user