From ebadcb97046dd1e024eb767b238866f8a18db20b Mon Sep 17 00:00:00 2001 From: missytake Date: Mon, 13 May 2024 16:54:00 +0200 Subject: [PATCH] nginx: add nginx_deployer to reload nginx only once --- pyinfra_nginx/__init__.py | 2 +- pyinfra_nginx/nginx.py | 189 ++++++++++++++++++++------------------ 2 files changed, 100 insertions(+), 91 deletions(-) diff --git a/pyinfra_nginx/__init__.py b/pyinfra_nginx/__init__.py index 22f166d..285d6e1 100644 --- a/pyinfra_nginx/__init__.py +++ b/pyinfra_nginx/__init__.py @@ -1 +1 @@ -from .nginx import deploy_nginx, add_nginx_domain +from .nginx import deploy_nginx, nginx_deployer, NGINX diff --git a/pyinfra_nginx/nginx.py b/pyinfra_nginx/nginx.py index be9005c..2d23f7f 100644 --- a/pyinfra_nginx/nginx.py +++ b/pyinfra_nginx/nginx.py @@ -1,9 +1,8 @@ -from io import StringIO +import contextlib import importlib.resources -from pyinfra import host -from pyinfra.api.deploy import deploy -from pyinfra.operations import files, server, apt, systemd +from pyinfra import host, logger +from pyinfra.operations import files, apt, systemd from pyinfra.facts.deb import DebPackages from pyinfra_acmetool import deploy_acmetool @@ -20,87 +19,105 @@ def deploy_nginx(): ) -def add_nginx_domain( - domain: str, - config_path: str = None, - webroot: str = None, - proxy_port: int = None, - redirect: str = None, - enabled=True, - acmetool=True, - skip_restart=False, -): - """Let a domain be handled by nginx, create a Let's Encrypt certificate for it, and deploy the config. - - This method supports 3 template configs for configuring your site: - - "webroot" for serving a static page, - - "proxy_port" for passing traffic to a separate application listening on some port, - - and "redirect" for redirecting to a different website with a 301 HTTP status code. - - You can use "config_path" if your site is so special it needs a custom config. - These 4 options are mutually exclusive. - - :param domain: the domain of the website - :param config_path: the local path to the nginx config file - :param webroot: path to a webroot directory, e.g. /var/www/staging/. Generates its own config from template. - :param proxy_port: proxy_pass all HTTP traffic to some internal port - :param redirect: where to 301 redirect to, e.g. https://i.delta.chat$request_uri - :param enabled: whether the site should be enabled at /etc/nginx/sites-enabled - :param acmetool: whether acmetool should fetch TLS certs for the domain - :param skip_restart: set True if the nginx restart is done later anyway - """ - default_config_link = files.link( - path="/etc/nginx/sites-enabled/default", present=False +@contextlib.contextmanager +def nginx_deployer(reload_nginx: bool = False): + nginx = NGINX(reload_nginx) + yield nginx + systemd.service( + name="enable and start NGINX service", + service="nginx.service", + running=True, + enabled=True, + reloaded=nginx.reload, ) - if default_config_link.changed: - systemd.service( - name="reload nginx", - service="nginx.service", - reloaded=True, + + +class NGINX: + def __init__(self, reload): + self.reload = reload + + def add_nginx_domain( + self, + domain: str, + config_path: str = None, + webroot: str = None, + proxy_port: int = None, + redirect: str = None, + enabled=True, + acmetool=True, + ) -> bool: + """Let a domain be handled by nginx, create a Let's Encrypt certificate for it, and deploy the config. + + This method supports 3 template configs for configuring your site: + - "webroot" for serving a static page, + - "proxy_port" for passing traffic to a separate application listening on some port, + - and "redirect" for redirecting to a different website with a 301 HTTP status code. + - You can use "config_path" if your site is so special it needs a custom config. + These 4 options are mutually exclusive. + + :param domain: the domain of the website + :param config_path: the local path to the nginx config file + :param webroot: path to a webroot directory, e.g. /var/www/staging/. Generates its own config from template. + :param proxy_port: proxy_pass all HTTP traffic to some internal port + :param redirect: where to 301 redirect to, e.g. https://i.delta.chat$request_uri + :param enabled: whether the site should be enabled at /etc/nginx/sites-enabled + :param acmetool: whether acmetool should fetch TLS certs for the domain + :return whether the nginx config was changed and needs a reload + """ + default_config_link = files.link( + path="/etc/nginx/sites-enabled/default", present=False ) + self.reload |= default_config_link.changed - if acmetool: - deploy_acmetool(nginx_hook=True, domains=[domain]) + if acmetool: + deploy_acmetool(nginx_hook=True, domains=[domain]) + + if enabled: + if config_path: + config = files.put( + src=config_path, + dest=f"/etc/nginx/sites-available/{domain}", + user="root", + group="root", + mode="644", + ) + elif webroot: + config = files.template( + src=importlib.resources.files(__package__) / "webroot.nginx_config.j2", + dest=f"/etc/nginx/sites-available/{domain}", + user="root", + group="root", + mode="644", + webroot=webroot, + domain=domain, + ) + elif proxy_port: + config = files.template( + src=importlib.resources.files(__package__) + / "proxy_pass.nginx_config.j2", + dest=f"/etc/nginx/sites-available/{domain}", + user="root", + group="root", + mode="644", + domain=domain, + proxy_port=proxy_port, + ) + elif redirect: + config = files.template( + src=importlib.resources.files(__package__) / "redirect.nginx_config.j2", + dest=f"/etc/nginx/sites-available/{domain}", + user="root", + group="root", + mode="644", + domain=domain, + redirect=redirect, + ) + try: + self.reload |= config.changed + except AttributeError: + logger.error("please pass either webroot, proxy_port, redirect, or config_path to add_nginx_domain") + raise - if enabled: - if config_path: - config = files.put( - src=config_path, - dest=f"/etc/nginx/sites-available/{domain}", - user="root", - group="root", - mode="644", - ) - elif webroot: - config = files.template( - src=importlib.resources.files(__package__) / "webroot.nginx_config.j2", - dest=f"/etc/nginx/sites-available/{domain}", - user="root", - group="root", - mode="644", - webroot=webroot, - domain=domain, - ) - elif proxy_port: - config = files.template( - src=importlib.resources.files(__package__) - / "proxy_pass.nginx_config.j2", - dest=f"/etc/nginx/sites-available/{domain}", - user="root", - group="root", - mode="644", - domain=domain, - proxy_port=proxy_port, - ) - elif redirect: - config = files.template( - src=importlib.resources.files(__package__) / "redirect.nginx_config.j2", - dest=f"/etc/nginx/sites-available/{domain}", - user="root", - group="root", - mode="644", - domain=domain, - redirect=redirect, - ) config_link = files.link( path=f"/etc/nginx/sites-enabled/{domain}", target=f"/etc/nginx/sites-available/{domain}", @@ -108,12 +125,4 @@ def add_nginx_domain( group="root", present=enabled, ) - if not skip_restart: - if config.changed or config_link.changed: - systemd.service( - name="NGINX should be enabled and running", - service="nginx.service", - running=True, - enabled=True, - restarted=True, - ) + self.reload |= config_link.changed