From 84c8288f8a19b947d3e9beef8fce385f186c902b Mon Sep 17 00:00:00 2001 From: Alexandre Pires Date: Mon, 6 Oct 2025 23:36:50 +0200 Subject: [PATCH] feat: implement getmail runner script and update email fetching configuration --- terraform/apps/prod-01/apps.tf | 2 - terraform/modules/apps/mail/config.tf | 4 + terraform/modules/apps/mail/getmail.tf | 158 ++++++++++++------ .../apps/mail/resources/getmail/runner.py | 44 +++++ terraform/modules/apps/mail/variables.tf | 7 +- 5 files changed, 157 insertions(+), 58 deletions(-) create mode 100644 terraform/modules/apps/mail/resources/getmail/runner.py diff --git a/terraform/apps/prod-01/apps.tf b/terraform/apps/prod-01/apps.tf index acd59cd..4603cd7 100644 --- a/terraform/apps/prod-01/apps.tf +++ b/terraform/apps/prod-01/apps.tf @@ -48,8 +48,6 @@ module "mail" { relay_smtp_username = var.scaleway_project_id relay_smtp_password = local.mail_app_api_secret_key - fetch_emails_schedule = local.fetch_emails_schedule - mail_crypt_private_key = var.mail_crypt_private_key mail_crypt_public_key = var.mail_crypt_public_key diff --git a/terraform/modules/apps/mail/config.tf b/terraform/modules/apps/mail/config.tf index 0f3c343..2c0853f 100644 --- a/terraform/modules/apps/mail/config.tf +++ b/terraform/modules/apps/mail/config.tf @@ -101,6 +101,10 @@ locals { secret_name = "mail-tls" } } + + getmail_runner_script = file("${path.module}/resources/getmail/runner.py") + checksum_getmail_runner_script = sha256(local.getmail_runner_script) + getmail_fetch_interval_seconds = coalesce(var.getmail_fetch_interval_seconds, 120) } data "external" "password_hasher" { diff --git a/terraform/modules/apps/mail/getmail.tf b/terraform/modules/apps/mail/getmail.tf index bcbe276..969d1c6 100644 --- a/terraform/modules/apps/mail/getmail.tf +++ b/terraform/modules/apps/mail/getmail.tf @@ -8,80 +8,132 @@ resource "kubernetes_secret" "getmail_config" { type = "Opaque" } -resource "kubernetes_cron_job_v1" "fetch_emails" { +resource "kubernetes_config_map_v1" "getmail_runner" { + metadata { + name = "getmail-runner" + namespace = kubernetes_namespace.mail.metadata[0].name + } + data = { + "runner.py" = local.getmail_runner_script + } +} + +resource "kubernetes_deployment_v1" "fetch_emails" { metadata { namespace = kubernetes_namespace.mail.metadata[0].name name = "fetch-emails" + labels = { + app = "fetch-emails" + } } spec { - schedule = var.fetch_emails_schedule - - job_template { + replicas = 1 + selector { + match_labels = { + app = "fetch-emails" + } + } + template { metadata { - name = "fetch-emails" + labels = { + app = "fetch-emails" + } + annotations = { + "checksum/runner-script" = local.checksum_getmail_runner_script + } } spec { - backoff_limit = 1 - parallelism = 1 - completions = 1 - active_deadline_seconds = 300 - - template { - metadata { - labels = { - app = "fetch-emails" - } + container { + name = "getmail" + image = "a13labs/getmail6:${var.getmail_tag}" + command = ["python3", "/runner/runner.py"] + env { + name = "FETCH_INTERVAL_SECONDS" + value = local.getmail_fetch_interval_seconds } - spec { - container { - name = "getmail" - image = "a13labs/getmail6:${var.getmail_tag}" - args = concat(["--getmaildir", "/var/getmail"], flatten([for job in local.getmail_jobs : ["-r", "/getmail/getmailrc_${job.name}"]])) + volume_mount { + name = "getmail-config" + mount_path = "/getmail" + read_only = true + } + volume_mount { + name = "getmail-cache" + mount_path = "/var/getmail" + } + volume_mount { + name = "runner-script" + mount_path = "/runner" + read_only = true + } + volume_mount { + name = "tmp" + mount_path = "/tmp" + } - volume_mount { - name = "getmail-config" - mount_path = "/getmail" - read_only = true - } + security_context { + run_as_user = 1000 + run_as_group = 1000 + read_only_root_filesystem = true + } - security_context { - run_as_user = 1000 - run_as_group = 1000 - } - - volume_mount { - name = "getmail-cache" - mount_path = "/var/getmail" - } + # Basic liveness (process must still be running) + liveness_probe { + exec { + command = ["bash", "-c", "pgrep -f runner.py > /dev/null"] } + initial_delay_seconds = 30 + period_seconds = 30 + } - restart_policy = "Never" - - volume { - name = "getmail-config" - secret { - secret_name = kubernetes_secret.getmail_config.metadata[0].name - dynamic "items" { - for_each = local.getmail_jobs - content { - key = "getmailrc_${items.value.name}" - path = "getmailrc_${items.value.name}" - } - - } - } + resources { + requests = { + cpu = "20m" + memory = "64Mi" } + limits = { + cpu = "200m" + memory = "256Mi" + } + } + } - volume { - name = "getmail-cache" - host_path { - path = local.getmail_path + volume { + name = "getmail-config" + secret { + secret_name = kubernetes_secret.getmail_config.metadata[0].name + dynamic "items" { + for_each = local.getmail_jobs + content { + key = "getmailrc_${items.value.name}" + path = "getmailrc_${items.value.name}" } } } } + + volume { + name = "getmail-cache" + host_path { + path = local.getmail_path + } + } + + volume { + name = "runner-script" + config_map { + name = kubernetes_config_map_v1.getmail_runner.metadata[0].name + items { + key = "runner.py" + path = "runner.py" + } + } + } + volume { + name = "tmp" + empty_dir {} + } } } } diff --git a/terraform/modules/apps/mail/resources/getmail/runner.py b/terraform/modules/apps/mail/resources/getmail/runner.py new file mode 100644 index 0000000..90b27ec --- /dev/null +++ b/terraform/modules/apps/mail/resources/getmail/runner.py @@ -0,0 +1,44 @@ +import os, signal, subprocess, time, sys +import logging + +INTERVAL = int(os.getenv("FETCH_INTERVAL_SECONDS", "120")) +GETMAILDIR = "/var/getmail" +CONFIG_DIR = "/getmail" +JOBS = [f for f in os.listdir(CONFIG_DIR) if f.startswith("getmailrc_")] + +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s] %(levelname)s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + +stop = False +def handle(sig, frame): + global stop + logging.info(f"Received signal {sig}, stopping runner...") + stop = True + +for s in (signal.SIGINT, signal.SIGTERM): + signal.signal(s, handle) + +def run_once(): + args = ["getmail", "--getmaildir", GETMAILDIR] + for job in JOBS: + args += ["-r", f"{CONFIG_DIR}/{job}"] + logging.info(f"Running getmail with jobs: {JOBS}") + try: + rc = subprocess.call(args, stdout=sys.stdout, stderr=sys.stderr) + logging.info(f"getmail exited with code {rc}") + return rc + except Exception as e: + logging.error(f"Error running getmail: {e}") + return 1 + +logging.info("Starting getmail runner...") +while not stop: + rc = run_once() + for _ in range(INTERVAL): + if stop: break + time.sleep(1) +logging.info("Exiting getmail runner.") +sys.exit(0) \ No newline at end of file diff --git a/terraform/modules/apps/mail/variables.tf b/terraform/modules/apps/mail/variables.tf index c9bf195..1ae538f 100644 --- a/terraform/modules/apps/mail/variables.tf +++ b/terraform/modules/apps/mail/variables.tf @@ -46,9 +46,10 @@ variable "relay_smtp_password" { sensitive = true } -variable "fetch_emails_schedule" { - description = "The cron schedule for fetching emails" - type = string +variable "getmail_fetch_interval_seconds" { + description = "The interval for fetching emails (in seconds)" + type = number + default = 120 } variable "getmail_tag" {