1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-20 05:11:30 +00:00

Use config.json to render multiple Jinja2 templates

This commit is contained in:
FreddleSpl0it
2025-05-22 14:07:39 +02:00
parent 13b4f86d29
commit dba9675a9b
18 changed files with 329 additions and 88 deletions

View File

@@ -30,30 +30,53 @@ class BootstrapBase:
self.mysql_conn = None
self.redis_conn = None
def render_config(self, template_name, output_path, clean_blank_lines=False):
def render_config(self, config_file):
"""
Renders a Jinja2 template and writes it to the specified output path.
Renders multiple Jinja2 templates based on a JSON config file.
Args:
template_name (str): Name of the template file.
output_path (str or Path): Path to write the rendered output file.
clean_blank_lines (bool): If True, removes empty/whitespace-only lines from rendered output.
Each config entry must include:
- template (str): the template filename
- output (str): absolute path to the output file
Optional:
- clean_blank_lines (bool): remove empty lines from output
- if_not_exists (bool): skip rendering if output file already exists
"""
output_path = Path(output_path)
output_path.parent.mkdir(parents=True, exist_ok=True)
from pathlib import Path
import json
template = self.env.get_template(template_name)
rendered = template.render(self.env_vars)
config_path = Path(config_file)
if not config_path.exists():
print(f"Template config file not found: {config_path}")
return
if clean_blank_lines:
rendered = "\n".join(line for line in rendered.splitlines() if line.strip())
with config_path.open("r") as f:
entries = json.load(f)
# converts output to Unix-style line endings
rendered = rendered.replace('\r\n', '\n').replace('\r', '\n')
for entry in entries:
template_name = entry["template"]
output_path = Path(entry["output"])
clean_blank_lines = entry.get("clean_blank_lines", False)
if_not_exists = entry.get("if_not_exists", False)
with open(output_path, "w") as f:
f.write(rendered)
if if_not_exists and output_path.exists():
print(f"Skipping {output_path} (already exists)")
continue
output_path.parent.mkdir(parents=True, exist_ok=True)
template = self.env.get_template(template_name)
rendered = template.render(self.env_vars)
if clean_blank_lines:
rendered = "\n".join(line for line in rendered.splitlines() if line.strip())
rendered = rendered.replace('\r\n', '\n').replace('\r', '\n')
with output_path.open("w") as f:
f.write(rendered)
print(f"Rendered {template_name} to {output_path}")
def prepare_template_vars(self, overwrite_path, extra_vars = None):
"""

View File

@@ -47,7 +47,7 @@ class Bootstrap(BootstrapBase):
self.set_timezone()
print("Render config")
self.render_config("whitelist.ign2.j2", "/var/lib/clamav/whitelist.ign2", clean_blank_lines=True)
self.render_config("/etc/clamav/config.json")
# Fix permissions
self.set_owner("/var/lib/clamav", "clamav", "clamav", recursive=True)

View File

@@ -55,23 +55,7 @@ class Bootstrap(BootstrapBase):
self.set_timezone()
print("Render config")
self.render_config("dovecot-dict-sql-quota.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-quota.conf")
self.render_config("dovecot-dict-sql-userdb.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-userdb.conf")
self.render_config("dovecot-dict-sql-sieve_before.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf")
self.render_config("dovecot-dict-sql-sieve_after.conf.j2", "/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf")
self.render_config("mail_plugins.j2", "/etc/dovecot/mail_plugins")
self.render_config("mail_plugins_imap.j2", "/etc/dovecot/mail_plugins_imap")
self.render_config("mail_plugins_lmtp.j2", "/etc/dovecot/mail_plugins_lmtp")
self.render_config("global_sieve_after.sieve.j2", "/var/vmail/sieve/global_sieve_after.sieve")
self.render_config("global_sieve_before.sieve.j2", "/var/vmail/sieve/global_sieve_before.sieve")
self.render_config("dovecot-master.passwd.j2", "/etc/dovecot/dovecot-master.passwd")
self.render_config("dovecot-master.userdb.j2", "/etc/dovecot/dovecot-master.userdb")
self.render_config("sieve.creds.j2", "/etc/sogo/sieve.creds")
self.render_config("sogo-sso.pass.j2", "/etc/phpfpm/sogo-sso.pass")
self.render_config("cron.creds.j2", "/etc/sogo/cron.creds")
self.render_config("source_env.sh.j2", "/source_env.sh")
self.render_config("maildir_gc.sh.j2", "/usr/local/bin/maildir_gc.sh")
self.render_config("dovecot.conf.j2", "/etc/dovecot/dovecot.conf")
self.render_config("/etc/dovecot/config.json")
files = [
"/etc/dovecot/mail_plugins",

View File

@@ -46,7 +46,7 @@ class Bootstrap(BootstrapBase):
self.set_timezone()
print("Render config")
self.render_config("my.cnf.j2", "/etc/mysql/conf.d/my.cnf")
self.render_config("/etc/mysql/conf.d/config.json")
def start_temporary(self, socket):
"""

View File

@@ -41,11 +41,7 @@ class Bootstrap(BootstrapBase):
self.set_timezone()
print("Render config")
self.render_config("nginx.conf.j2", "/etc/nginx/nginx.conf")
self.render_config("sites-default.conf.j2", "/etc/nginx/includes/sites-default.conf")
self.render_config("server_name.active.j2", "/etc/nginx/conf.d/server_name.active")
self.render_config("listen_plain.active.j2", "/etc/nginx/conf.d/listen_plain.active")
self.render_config("listen_ssl.active.j2", "/etc/nginx/conf.d/listen_ssl.active")
self.render_config("/etc/nginx/conf.d/config.json")
def get_valid_cert_dirs(self):
ssl_dir = '/etc/ssl/mail/'

View File

@@ -44,12 +44,7 @@ class Bootstrap(BootstrapBase):
print("Render config")
self.render_config("opcache-recommended.ini.j2", "/usr/local/etc/php/conf.d/opcache-recommended.ini")
self.render_config("pools.conf.j2", "/usr/local/etc/php-fpm.d/z-pools.conf")
self.render_config("other.ini.j2", "/usr/local/etc/php/conf.d/zzz-other.ini")
self.render_config("upload.ini.j2", "/usr/local/etc/php/conf.d/upload.ini")
self.render_config("session_store.ini.j2", "/usr/local/etc/php/conf.d/session_store.ini")
self.render_config("0081-custom-mailcow.css.j2", "/web/css/build/0081-custom-mailcow.css")
self.render_config("/php-conf/config.json")
self.copy_file("/usr/local/etc/php/conf.d/opcache-recommended.ini", "/php-conf/opcache-recommended.ini")
self.copy_file("/usr/local/etc/php-fpm.d/z-pools.conf", "/php-conf/pools.conf")

View File

@@ -40,39 +40,7 @@ class Bootstrap(BootstrapBase):
self.set_syslog_redis()
print("Render config")
self.render_config("aliases.j2", "/etc/aliases")
self.render_config("mysql_relay_ne.cf.j2", "/opt/postfix/conf/sql/mysql_relay_ne.cf")
self.render_config("mysql_relay_recipient_maps.cf.j2", "/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf")
self.render_config("mysql_tls_policy_override_maps.cf.j2", "/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf")
self.render_config("mysql_tls_enforce_in_policy.cf.j2", "/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf")
self.render_config("mysql_sender_dependent_default_transport_maps.cf.j2", "/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf")
self.render_config("mysql_transport_maps.cf.j2", "/opt/postfix/conf/sql/mysql_transport_maps.cf")
self.render_config("mysql_virtual_resource_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_resource_maps.cf")
self.render_config("mysql_sasl_passwd_maps_sender_dependent.cf.j2", "/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf")
self.render_config("mysql_sasl_passwd_maps_transport_maps.cf.j2", "/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf")
self.render_config("mysql_virtual_alias_domain_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf")
self.render_config("mysql_virtual_alias_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_alias_maps.cf")
self.render_config("mysql_recipient_bcc_maps.cf.j2", "/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf")
self.render_config("mysql_sender_bcc_maps.cf.j2", "/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf")
self.render_config("mysql_recipient_canonical_maps.cf.j2", "/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf")
self.render_config("mysql_virtual_domains_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf")
self.render_config("mysql_virtual_mailbox_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf")
self.render_config("mysql_virtual_relay_domain_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf")
self.render_config("mysql_virtual_sender_acl.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf")
self.render_config("mysql_mbr_access_maps.cf.j2", "/opt/postfix/conf/sql/mysql_mbr_access_maps.cf")
self.render_config("mysql_virtual_spamalias_maps.cf.j2", "/opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf")
self.render_config("sni.map.j2", "/opt/postfix/conf/sni.map")
self.render_config("main.cf.j2", "/opt/postfix/conf/main.cf")
# Conditional render
if not Path("/opt/postfix/conf/dns_blocklists.cf").exists():
self.render_config("dns_blocklists.cf.j2", "/opt/postfix/conf/dns_blocklists.cf")
if not Path("/opt/postfix/conf/dns_reply.map").exists():
self.render_config("dns_reply.map.j2", "/opt/postfix/conf/dns_reply.map")
if not Path("/opt/postfix/conf/custom_postscreen_whitelist.cidr").exists():
self.render_config("custom_postscreen_whitelist.cidr.j2", "/opt/postfix/conf/custom_postscreen_whitelist.cidr")
if not Path("/opt/postfix/conf/custom_transport.pcre").exists():
self.render_config("custom_transport.pcre.j2", "/opt/postfix/conf/custom_transport.pcre")
self.render_config("/opt/postfix/conf/config.json")
# Create SNI Config
self.run_command(["postmap", "-F", "hash:/opt/postfix/conf/sni.map"])

View File

@@ -80,13 +80,7 @@ class Bootstrap(BootstrapBase):
self.set_timezone()
print("Render config")
self.render_config("mailcow_networks.map.j2", "/etc/rspamd/custom/mailcow_networks.map")
self.render_config("dovecot_trusted.map.j2", "/etc/rspamd/custom/dovecot_trusted.map")
self.render_config("rspamd_trusted.map.j2", "/etc/rspamd/custom/rspamd_trusted.map")
self.render_config("external_services.conf.j2", "/etc/rspamd/local.d/external_services.conf")
self.render_config("redis.conf.j2", "/etc/rspamd/local.d/redis.conf")
self.render_config("dqs-rbl.conf.j2", "/etc/rspamd/custom/dqs-rbl.conf")
self.render_config("worker-controller-password.inc.j2", "/etc/rspamd/override.d/worker-controller-password.inc")
self.render_config("/etc/rspamd/config.json")
# Fix missing default global maps, if any
# These exists in mailcow UI and should not be removed

View File

@@ -48,8 +48,7 @@ class Bootstrap(BootstrapBase):
self.set_syslog_redis()
print("Render config")
self.render_config("sogod.plist.j2", "/var/lib/sogo/GNUstep/Defaults/sogod.plist")
self.render_config("UIxTopnavToolbar.wox.j2", "/usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox")
self.render_config("/etc/sogo/config.json")
print("Fix permissions")
self.set_owner("/var/lib/sogo", "sogo", "sogo", recursive=True)

View File

@@ -0,0 +1,7 @@
[
{
"template": "whitelist.ign2.j2",
"output": "/var/lib/clamav/whitelist.ign2",
"clean_blank_lines": true
}
]

View File

@@ -0,0 +1,70 @@
[
{
"template": "dovecot-dict-sql-quota.conf.j2",
"output": "/etc/dovecot/sql/dovecot-dict-sql-quota.conf"
},
{
"template": "dovecot-dict-sql-userdb.conf.j2",
"output": "/etc/dovecot/sql/dovecot-dict-sql-userdb.conf"
},
{
"template": "dovecot-dict-sql-sieve_before.conf.j2",
"output": "/etc/dovecot/sql/dovecot-dict-sql-sieve_before.conf"
},
{
"template": "dovecot-dict-sql-sieve_after.conf.j2",
"output": "/etc/dovecot/sql/dovecot-dict-sql-sieve_after.conf"
},
{
"template": "mail_plugins.j2",
"output": "/etc/dovecot/mail_plugins"
},
{
"template": "mail_plugins_imap.j2",
"output": "/etc/dovecot/mail_plugins_imap"
},
{
"template": "mail_plugins_lmtp.j2",
"output": "/etc/dovecot/mail_plugins_lmtp"
},
{
"template": "global_sieve_after.sieve.j2",
"output": "/var/vmail/sieve/global_sieve_after.sieve"
},
{
"template": "global_sieve_before.sieve.j2",
"output": "/var/vmail/sieve/global_sieve_before.sieve"
},
{
"template": "dovecot-master.passwd.j2",
"output": "/etc/dovecot/dovecot-master.passwd"
},
{
"template": "dovecot-master.userdb.j2",
"output": "/etc/dovecot/dovecot-master.userdb"
},
{
"template": "sieve.creds.j2",
"output": "/etc/sogo/sieve.creds"
},
{
"template": "sogo-sso.pass.j2",
"output": "/etc/phpfpm/sogo-sso.pass"
},
{
"template": "cron.creds.j2",
"output": "/etc/sogo/cron.creds"
},
{
"template": "source_env.sh.j2",
"output": "/source_env.sh"
},
{
"template": "maildir_gc.sh.j2",
"output": "/usr/local/bin/maildir_gc.sh"
},
{
"template": "dovecot.conf.j2",
"output": "/etc/dovecot/dovecot.conf"
}
]

View File

@@ -0,0 +1,6 @@
[
{
"template": "my.cnf.j2",
"output": "/etc/mysql/conf.d/my.cnf"
}
]

View File

@@ -0,0 +1,22 @@
[
{
"template": "nginx.conf.j2",
"output": "/etc/nginx/nginx.conf"
},
{
"template": "sites-default.conf.j2",
"output": "/etc/nginx/includes/sites-default.conf"
},
{
"template": "server_name.active.j2",
"output": "/etc/nginx/conf.d/server_name.active"
},
{
"template": "listen_plain.active.j2",
"output": "/etc/nginx/conf.d/listen_plain.active"
},
{
"template": "listen_ssl.active.j2",
"output": "/etc/nginx/conf.d/listen_ssl.active"
}
]

View File

@@ -0,0 +1,26 @@
[
{
"template": "opcache-recommended.ini.j2",
"output": "/usr/local/etc/php/conf.d/opcache-recommended.ini"
},
{
"template": "pools.conf.j2",
"output": "/usr/local/etc/php-fpm.d/z-pools.conf"
},
{
"template": "other.ini.j2",
"output": "/usr/local/etc/php/conf.d/zzz-other.ini"
},
{
"template": "upload.ini.j2",
"output": "/usr/local/etc/php/conf.d/upload.ini"
},
{
"template": "session_store.ini.j2",
"output": "/usr/local/etc/php/conf.d/session_store.ini"
},
{
"template": "0081-custom-mailcow.css.j2",
"output": "/web/css/build/0081-custom-mailcow.css"
}
]

View File

@@ -0,0 +1,110 @@
[
{
"template": "aliases.j2",
"output": "/etc/aliases"
},
{
"template": "mysql_relay_ne.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_relay_ne.cf"
},
{
"template": "mysql_relay_recipient_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_relay_recipient_maps.cf"
},
{
"template": "mysql_tls_policy_override_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_tls_policy_override_maps.cf"
},
{
"template": "mysql_tls_enforce_in_policy.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf"
},
{
"template": "mysql_sender_dependent_default_transport_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_sender_dependent_default_transport_maps.cf"
},
{
"template": "mysql_transport_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_transport_maps.cf"
},
{
"template": "mysql_virtual_resource_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_resource_maps.cf"
},
{
"template": "mysql_sasl_passwd_maps_sender_dependent.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf"
},
{
"template": "mysql_sasl_passwd_maps_transport_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf"
},
{
"template": "mysql_virtual_alias_domain_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_alias_domain_maps.cf"
},
{
"template": "mysql_virtual_alias_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_alias_maps.cf"
},
{
"template": "mysql_recipient_bcc_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf"
},
{
"template": "mysql_sender_bcc_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf"
},
{
"template": "mysql_recipient_canonical_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf"
},
{
"template": "mysql_virtual_domains_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf"
},
{
"template": "mysql_virtual_mailbox_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf"
},
{
"template": "mysql_virtual_relay_domain_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_relay_domain_maps.cf"
},
{
"template": "mysql_virtual_sender_acl.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_sender_acl.cf"
},
{
"template": "mysql_mbr_access_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_mbr_access_maps.cf"
},
{
"template": "mysql_virtual_spamalias_maps.cf.j2",
"output": "/opt/postfix/conf/sql/mysql_virtual_spamalias_maps.cf"
},
{
"template": "sni.map.j2",
"output": "/opt/postfix/conf/sni.map"
},
{
"template": "main.cf.j2",
"output": "/opt/postfix/conf/main.cf"
},
{
"template": "dns_blocklists.cf.j2",
"output": "/opt/postfix/conf/dns_blocklists.cf"
},
{
"template": "dns_reply.map.j2",
"output": "/opt/postfix/conf/dns_reply.map"
},
{
"template": "custom_postscreen_whitelist.cidr.j2",
"output": "/opt/postfix/conf/custom_postscreen_whitelist.cidr"
},
{
"template": "custom_transport.pcre.j2",
"output": "/opt/postfix/conf/custom_transport.pcre"
}
]

View File

@@ -0,0 +1,30 @@
[
{
"template": "mailcow_networks.map.j2",
"output": "/etc/rspamd/custom/mailcow_networks.map"
},
{
"template": "dovecot_trusted.map.j2",
"output": "/etc/rspamd/custom/dovecot_trusted.map"
},
{
"template": "rspamd_trusted.map.j2",
"output": "/etc/rspamd/custom/rspamd_trusted.map"
},
{
"template": "external_services.conf.j2",
"output": "/etc/rspamd/local.d/external_services.conf"
},
{
"template": "redis.conf.j2",
"output": "/etc/rspamd/local.d/redis.conf"
},
{
"template": "dqs-rbl.conf.j2",
"output": "/etc/rspamd/custom/dqs-rbl.conf"
},
{
"template": "worker-controller-password.inc.j2",
"output": "/etc/rspamd/override.d/worker-controller-password.inc"
}
]

View File

@@ -0,0 +1,10 @@
[
{
"template": "sogod.plist.j2",
"output": "/var/lib/sogo/GNUstep/Defaults/sogod.plist"
},
{
"template": "UIxTopnavToolbar.wox.j2",
"output": "/usr/lib/GNUstep/SOGo/Templates/UIxTopnavToolbar.wox"
}
]

View File

@@ -119,6 +119,7 @@ services:
- ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro,Z
- ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local:Z
- ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override:Z
- ./data/conf/rspamd/config.json:/etc/rspamd/config.json:Z
- mysql-socket-vol-1:/var/run/mysqld/
- rspamd-vol-1:/var/lib/rspamd
restart: always