From afd9bbf1fe83038b56736aa0cef184c0b331bf04 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Mon, 26 Jan 2015 21:34:44 +0100 Subject: [PATCH 1/9] add wildcard support to create_web_vhost_config task --- gvaweb/webtasks/tasks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gvaweb/webtasks/tasks.py b/gvaweb/webtasks/tasks.py index 6d71aa8..999c324 100644 --- a/gvaweb/webtasks/tasks.py +++ b/gvaweb/webtasks/tasks.py @@ -40,13 +40,15 @@ def _build_document_root_path(sitename, username): @shared_task -def create_web_vhost_config(username, sitename): +def create_web_vhost_config(username, sitename, wildcard): """ This task creates a virtual host configuration on an nginx web server. :param str username: user who owns the site :param str sitename: site name + :param boolean wildcard: designates whether this is website has a wildcard + subdomain :return: :py:const:`True` if the creation finished successfully :rtype: boolean From 549618bfad03e8a29d3527a5634939646d966bb1 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 13:48:00 +0100 Subject: [PATCH 2/9] first tasks implementation, added nginx and FPM templates --- gvaweb/webtasks/tasks.py | 115 +++++++++++++++++++++++++ gvaweb/webtasks/templates/fpmpool.conf | 16 ++++ gvaweb/webtasks/templates/vhost.nginx | 31 +++++++ requirements/base.txt | 1 + 4 files changed, 163 insertions(+) create mode 100644 gvaweb/webtasks/templates/fpmpool.conf create mode 100644 gvaweb/webtasks/templates/vhost.nginx diff --git a/gvaweb/webtasks/tasks.py b/gvaweb/webtasks/tasks.py index 999c324..064f741 100644 --- a/gvaweb/webtasks/tasks.py +++ b/gvaweb/webtasks/tasks.py @@ -5,6 +5,10 @@ This module defines Celery_ tasks to manage website configurations. from __future__ import absolute_import import os +import subprocess +from tempfile import mkstemp + +from jinja2 import Environment, PackageLoader from celery import shared_task from celery.utils.log import get_task_logger @@ -20,6 +24,8 @@ LN_CMD = '/bin/ln' SERVICE_CMD = '/usr/sbin/service' INSTALL_CMD = '/usr/bin/install' +JINJAENV = Environment(loader=PackageLoader('webtasks', 'templates')) + def _build_vhost_config_path(sitename): return os.path.join(settings.GVAWEB_NGINX_SITES_AVAILABLE, sitename) @@ -39,6 +45,10 @@ def _build_document_root_path(sitename, username): settings.GVAWEB_WWWUSER_MOUNT, username, sitename, 'html') +def _get_template(templatename): + return JINJAENV.get_template(templatename) + + @shared_task def create_web_vhost_config(username, sitename, wildcard): """ @@ -53,6 +63,32 @@ def create_web_vhost_config(username, sitename, wildcard): :rtype: boolean """ + conftmpl = _get_template('vhost.nginx') + confdata = conftmpl.render( + domain=sitename, user=username, + docroot=_build_document_root_path(sitename, username), + wildcard=wildcard) + try: + nginxtemp, filename = mkstemp() + conffile = os.fdopen(nginxtemp, 'w') + conffile.write(confdata.encode('utf8')) + finally: + if conffile: + conffile.close() + os.close(nginxtemp) + try: + subprocess.check_output([ + SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', 'root', '-m', '0640', + filename, _build_vhost_config_path(sitename)], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _LOGGER.exception( + 'could not setup site configuration for %s', sitename) + raise Exception( + 'could not setup site configuration for %s' % sitename) + return True @shared_task @@ -65,6 +101,19 @@ def disable_web_vhost(sitename): :rtype: boolean """ + try: + subprocess.check_output([ + SUDO_CMD, RM_CMD, _build_enabled_vhost_path(sitename)], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, SERVICE_CMD, 'nginx', 'reload'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _LOGGER.exception( + 'could not disable site configuration for %s', sitename) + raise Exception( + 'could not disable site configuration for %s' % sitename) + return True @shared_task @@ -78,6 +127,22 @@ def enable_web_vhost(sitename): :rtype: boolean """ + try: + subprocess.check_output([ + SUDO_CMD, LN_CMD, '-r', '-s', + _build_vhost_config_path(sitename), + _build_enabled_vhost_path(sitename)], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, SERVICE_CMD, 'nginx', 'restart'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _LOGGER.exception( + 'could not enable site configuration for %s', sitename) + raise Exception( + 'could not enable site configuration for %s' % sitename) + return True + @shared_task def delete_web_vhost_config(sitename): @@ -89,6 +154,16 @@ def delete_web_vhost_config(sitename): :rtype: boolean """ + try: + subprocess.check_output([ + SUDO_CMD, RM_CMD, _build_vhost_config_path(sitename)], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _LOGGER.exception( + 'could not delete site configuration for %s', sitename) + raise Exception( + 'could not delete site configuration for %s' % sitename) + return True @shared_task @@ -101,6 +176,33 @@ def create_web_php_fpm_pool_config(username): :rtype: boolean """ + conftmpl = _get_template('fpmpool.conf') + confdata = conftmpl.render(user=username) + try: + fpmtemp, filename = mkstemp() + conffile = os.fdopen(fpmtemp, 'w') + conffile.write(confdata.encode('utf8')) + finally: + if conffile: + conffile.close() + os.close(fpmtemp) + try: + subprocess.check_output([ + SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', 'root', '-m', '0644', + filename, _build_php_fpm_pool_file(username)], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, SERVICE_CMD, 'php5-fpm', 'reload'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _LOGGER.exception( + 'could not configure PHP FPM for %s', username) + raise Exception( + 'could not configure PHP FPM for %s' % username) + return True + @shared_task def delete_web_php_fpm_pool_config(username): @@ -112,3 +214,16 @@ def delete_web_php_fpm_pool_config(username): :rtype: boolean """ + try: + subprocess.check_output([ + SUDO_CMD, RM_CMD, _build_php_fpm_pool_file(username)], + stderr=subprocess.STDOUT) + subprocess.check_output([ + SUDO_CMD, SERVICE_CMD, 'php5-fpm', 'reload'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + _LOGGER.exception( + 'could not delete PHP FPM configuration for %s', username) + raise Exception( + 'could not delete PHP FPM configuration for %s', username) + return True diff --git a/gvaweb/webtasks/templates/fpmpool.conf b/gvaweb/webtasks/templates/fpmpool.conf new file mode 100644 index 0000000..718190c --- /dev/null +++ b/gvaweb/webtasks/templates/fpmpool.conf @@ -0,0 +1,16 @@ +[{{ user }}] +user = {{ user }} +group = {{ user }} +listen = /var/run/php5-fpm-{{ user }}.sock +listen.owner = www-data +listen.group = www-data +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +pm.max_requests = 1000 +chdir = / +request_slowlog_timeout = 10s +slowlog = /var/log/php5-fpm/{{ user }}.slow.log +error_log = /var/log/php5-fpm/{{ user }}.error.log diff --git a/gvaweb/webtasks/templates/vhost.nginx b/gvaweb/webtasks/templates/vhost.nginx new file mode 100644 index 0000000..caf78ad --- /dev/null +++ b/gvaweb/webtasks/templates/vhost.nginx @@ -0,0 +1,31 @@ +server { + server_name {{ domain }}; + {%- if wildcard %} + server_name *.{{ domain|parentdomain }}; + {%- endif %} + listen 80; + listen [::] 80; + + access_log /var/log/nginx/{{ domainname }}.access.log; + error_log /var/log/nginx/{{ domainname }}.error.log; + + client_max_body_size 20M; + gzip on; + gzip_types text/javascript application/x-javascript text/css; + + root {{ docroot }}; + + index index.php index.html index.htm; + + location ~ ^/(.+\.php)$ { + try_files $uri =404; + fastcgi_pass unix:/var/run/php5-fpm-{{ user }}.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_scriptname; + include fastcgi_params; + } + + location ~ /\.ht { + deny all; + } +} diff --git a/requirements/base.txt b/requirements/base.txt index 00c89eb..fa14272 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -5,3 +5,4 @@ billiard==3.3.0.19 celery==3.1.17 kombu==3.0.24 pytz==2014.10 +Jinja2==2.7.3 From a15843b0d5865881ec152e74602007469ab7e663 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 13:58:57 +0100 Subject: [PATCH 3/9] don't try to close tempfiles twice --- gvaweb/webtasks/tasks.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gvaweb/webtasks/tasks.py b/gvaweb/webtasks/tasks.py index 064f741..3331f97 100644 --- a/gvaweb/webtasks/tasks.py +++ b/gvaweb/webtasks/tasks.py @@ -75,7 +75,6 @@ def create_web_vhost_config(username, sitename, wildcard): finally: if conffile: conffile.close() - os.close(nginxtemp) try: subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', 'root', '-m', '0640', @@ -185,7 +184,6 @@ def create_web_php_fpm_pool_config(username): finally: if conffile: conffile.close() - os.close(fpmtemp) try: subprocess.check_output([ SUDO_CMD, INSTALL_CMD, '-o', 'root', '-g', 'root', '-m', '0644', From 68c7e036b26ff0541cd8010b53e8bfa5288560e3 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 14:07:58 +0100 Subject: [PATCH 4/9] remove pool error_log directive which is only allowed globally --- gvaweb/webtasks/templates/fpmpool.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/gvaweb/webtasks/templates/fpmpool.conf b/gvaweb/webtasks/templates/fpmpool.conf index 718190c..66bb4c4 100644 --- a/gvaweb/webtasks/templates/fpmpool.conf +++ b/gvaweb/webtasks/templates/fpmpool.conf @@ -13,4 +13,3 @@ pm.max_requests = 1000 chdir = / request_slowlog_timeout = 10s slowlog = /var/log/php5-fpm/{{ user }}.slow.log -error_log = /var/log/php5-fpm/{{ user }}.error.log From 6b8c1710ca0e7e07b1b5e74461fbc73094e47851 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 14:18:13 +0100 Subject: [PATCH 5/9] implement Jinja filter to calculate the parent domain of a domain --- gvaweb/webtasks/tasks.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gvaweb/webtasks/tasks.py b/gvaweb/webtasks/tasks.py index 3331f97..713d05d 100644 --- a/gvaweb/webtasks/tasks.py +++ b/gvaweb/webtasks/tasks.py @@ -27,6 +27,13 @@ INSTALL_CMD = '/usr/bin/install' JINJAENV = Environment(loader=PackageLoader('webtasks', 'templates')) +def _jinja_parentdomain(domain): + return '.'.join(domain.split('.')[1:]) + + +JINJAENV.filters['parentdomain'] = _jinja_parentdomain + + def _build_vhost_config_path(sitename): return os.path.join(settings.GVAWEB_NGINX_SITES_AVAILABLE, sitename) From 0bd10d23ec6aae968e41deb86389f09d2e4e54ea Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 14:28:21 +0100 Subject: [PATCH 6/9] wheezy's /bin/ln does not support '-r' parameter --- gvaweb/webtasks/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gvaweb/webtasks/tasks.py b/gvaweb/webtasks/tasks.py index 713d05d..5872a73 100644 --- a/gvaweb/webtasks/tasks.py +++ b/gvaweb/webtasks/tasks.py @@ -135,7 +135,7 @@ def enable_web_vhost(sitename): """ try: subprocess.check_output([ - SUDO_CMD, LN_CMD, '-r', '-s', + SUDO_CMD, LN_CMD, '-s', _build_vhost_config_path(sitename), _build_enabled_vhost_path(sitename)], stderr=subprocess.STDOUT) From 35b102a7b78007d22dee133e3ca7acdb90797e10 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 14:32:14 +0100 Subject: [PATCH 7/9] fix listen directive --- gvaweb/webtasks/templates/vhost.nginx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gvaweb/webtasks/templates/vhost.nginx b/gvaweb/webtasks/templates/vhost.nginx index caf78ad..64f5675 100644 --- a/gvaweb/webtasks/templates/vhost.nginx +++ b/gvaweb/webtasks/templates/vhost.nginx @@ -4,7 +4,7 @@ server { server_name *.{{ domain|parentdomain }}; {%- endif %} listen 80; - listen [::] 80; + listen [::]:80; access_log /var/log/nginx/{{ domainname }}.access.log; error_log /var/log/nginx/{{ domainname }}.error.log; From 7dc3a634f772eaff9c191fcd6902a8348eb03c33 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 15:24:58 +0100 Subject: [PATCH 8/9] unify exception handling, fix nginx template --- gvaweb/webtasks/tasks.py | 53 ++++++++++++--------------- gvaweb/webtasks/templates/vhost.nginx | 8 ++-- 2 files changed, 26 insertions(+), 35 deletions(-) diff --git a/gvaweb/webtasks/tasks.py b/gvaweb/webtasks/tasks.py index 5872a73..38a112f 100644 --- a/gvaweb/webtasks/tasks.py +++ b/gvaweb/webtasks/tasks.py @@ -34,6 +34,12 @@ def _jinja_parentdomain(domain): JINJAENV.filters['parentdomain'] = _jinja_parentdomain +def log_and_raise(exception, message, *args): + logargs = list(args) + [exception.returncode, exception.output] + _LOGGER.error(message + "\nreturncode: %d\noutput:\n%s", *logargs) + raise Exception(message % args) + + def _build_vhost_config_path(sitename): return os.path.join(settings.GVAWEB_NGINX_SITES_AVAILABLE, sitename) @@ -89,11 +95,9 @@ def create_web_vhost_config(username, sitename, wildcard): stderr=subprocess.STDOUT) subprocess.check_output([ SUDO_CMD, RM_CMD, filename], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _LOGGER.exception( - 'could not setup site configuration for %s', sitename) - raise Exception( - 'could not setup site configuration for %s' % sitename) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not setup site configuration for %s', sitename) return True @@ -114,11 +118,9 @@ def disable_web_vhost(sitename): subprocess.check_output([ SUDO_CMD, SERVICE_CMD, 'nginx', 'reload'], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _LOGGER.exception( - 'could not disable site configuration for %s', sitename) - raise Exception( - 'could not disable site configuration for %s' % sitename) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not disable site configuration for %s', sitename) return True @@ -142,11 +144,9 @@ def enable_web_vhost(sitename): subprocess.check_output([ SUDO_CMD, SERVICE_CMD, 'nginx', 'restart'], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _LOGGER.exception( - 'could not enable site configuration for %s', sitename) - raise Exception( - 'could not enable site configuration for %s' % sitename) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not enable site configuration for %s', sitename) return True @@ -164,11 +164,9 @@ def delete_web_vhost_config(sitename): subprocess.check_output([ SUDO_CMD, RM_CMD, _build_vhost_config_path(sitename)], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _LOGGER.exception( - 'could not delete site configuration for %s', sitename) - raise Exception( - 'could not delete site configuration for %s' % sitename) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not delete site configuration for %s', sitename) return True @@ -201,11 +199,8 @@ def create_web_php_fpm_pool_config(username): subprocess.check_output([ SUDO_CMD, SERVICE_CMD, 'php5-fpm', 'reload'], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _LOGGER.exception( - 'could not configure PHP FPM for %s', username) - raise Exception( - 'could not configure PHP FPM for %s' % username) + except subprocess.CalledProcessError as cpe: + log_and_raise(cpe, 'could not configure PHP FPM for %s', username) return True @@ -226,9 +221,7 @@ def delete_web_php_fpm_pool_config(username): subprocess.check_output([ SUDO_CMD, SERVICE_CMD, 'php5-fpm', 'reload'], stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - _LOGGER.exception( - 'could not delete PHP FPM configuration for %s', username) - raise Exception( - 'could not delete PHP FPM configuration for %s', username) + except subprocess.CalledProcessError as cpe: + log_and_raise( + cpe, 'could not delete PHP FPM configuration for %s', username) return True diff --git a/gvaweb/webtasks/templates/vhost.nginx b/gvaweb/webtasks/templates/vhost.nginx index 64f5675..16fa179 100644 --- a/gvaweb/webtasks/templates/vhost.nginx +++ b/gvaweb/webtasks/templates/vhost.nginx @@ -3,11 +3,9 @@ server { {%- if wildcard %} server_name *.{{ domain|parentdomain }}; {%- endif %} - listen 80; - listen [::]:80; - access_log /var/log/nginx/{{ domainname }}.access.log; - error_log /var/log/nginx/{{ domainname }}.error.log; + access_log /var/log/nginx/{{ domain }}.access.log; + error_log /var/log/nginx/{{ domain }}.error.log; client_max_body_size 20M; gzip on; @@ -21,7 +19,7 @@ server { try_files $uri =404; fastcgi_pass unix:/var/run/php5-fpm-{{ user }}.sock; fastcgi_index index.php; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_scriptname; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } From b0e948822eaa29bf580567803e133942543d7c96 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Tue, 27 Jan 2015 15:30:18 +0100 Subject: [PATCH 9/9] add release version in changelog, update conf.py --- docs/changelog.rst | 5 +++++ docs/conf.py | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 6554c03..f8aaef4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,4 +1,9 @@ Changelog ========= +* :release:`0.1.0 <2015-01-27>` +* :feature:`-` add tasks to setup and delete per user PHP5 FPM pool + configurations +* :feature:`-` add tasks to setup, enable, disable and delete nginx virtual + host configurations * :support:`-` initial project setup diff --git a/docs/conf.py b/docs/conf.py index ddd7ec4..e7955e7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -60,9 +60,9 @@ copyright = u'2015, Jan Dittberner' # built documents. # # The short X.Y version. -version = '0.0' +version = '0.1' # The full version, including alpha/beta/rc tags. -release = '0.0.0' +release = '0.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages.