Files
computer-playbook/utils/docker/healthcheck/prefixes.py

65 lines
2.3 KiB
Python

"""Prefixes: shell that runs before a probe without judging the container.
A prefix does something on the way past -- sending a smoke mail is the only
one so far -- and deliberately cannot decide liveness. That separation is what
lets a probe stay the single verdict.
"""
from __future__ import annotations
from typing import Any
MAIL_MARKER = "/tmp/email_sent" # noqa: S108 container-internal path, not a host tmpfile
def mail_branch(context: dict[str, Any]) -> str:
"""The one-shot smoke mail the msmtp prefix puts in front of a probe.
Args:
context: the probe context; needs ``email_enabled`` to do anything, plus
``domain`` -- the EHLO identity msmtp otherwise defaults to
``localhost``, which a strict relay refuses -- and ``blackhole``
for the recipient.
Returns:
The shell prefix, or an empty string when email is disabled.
Two joins carry the whole design and neither is accidental. The branch ends
in ``; `` so a relay that refuses the message cannot decide liveness -- that
verdict belongs to the probe this prefixes, and coupling them once left live
web apps marked dead until the swarm converge gate gave up (ebdadd91b). The
``&&`` before ``touch`` stays, so a refused mail leaves no marker and the
next probe retries instead of the smoke test becoming one-shot.
"""
if not context.get("email_enabled"):
return ""
domain = context.get("domain", "")
blackhole = context.get("blackhole", "")
ehlo = f"--domain={domain} " if domain else ""
return (
f"if [ ! -f {MAIL_MARKER} ]; then "
f"echo 'Subject: testmessage from {domain}\\n\\nSUCCESSFULL' "
f"| msmtp {ehlo}-t {blackhole} && touch {MAIL_MARKER}; fi; "
)
class MsmtpPrefix:
"""Sends one test mail before the liveness probe it is composed with.
Not a probe: it never judges the container. Its timings are floors the
composition may raise the probe's to, because the first run has a relay
handshake in front of it.
"""
name = "msmtp"
timeout = "20s"
retries = 5
start_period = "120s"
@staticmethod
def render(context: dict[str, Any]) -> str:
return mail_branch(context)
PREFIXES: dict[str, type[MsmtpPrefix]] = {MsmtpPrefix.name: MsmtpPrefix}