From: Ɓukasz Rekucki Date: Tue, 8 Jun 2010 08:56:59 +0000 (+0200) Subject: Class-based deployment config. X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/1424bfacd82ffc238f90f1baa99388fc1ac9f2e7 Class-based deployment config. --- diff --git a/.gitignore b/.gitignore index a1290ede..0d555842 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,12 @@ localsettings.py dev.sqlite requirements.pybundle *~ +*.orig # Python garbage *.pyc .coverage +.coveragerc coverage.xml nosetests.xml pip-log.txt diff --git a/deployment.py b/deployment.py index 3553aa18..a9a4f580 100644 --- a/deployment.py +++ b/deployment.py @@ -1,64 +1,141 @@ -#!/srv/library/redakcja/pythonenv/bin/python from __future__ import with_statement import shutil import os import sys +import logging + +logging.basicConfig(stream=sys.stderr, format="%(levelname)s:: %(message)s") from string import Template -def render_template(source, dest, context={}): - print "Rendering template:", - with open(source, 'rb') as source_file: - t = Template(source_file.read()) - with open(dest, 'wb') as dest_file: - dest_file.write(t.safe_substitute(context)) - print "done." +class DeploySite(object): + + def __init__(self, **env): + self.env = env + + for arg in ('ROOT', 'PROJECT_NAME', 'PYTHON_VERSION'): + if arg not in self.env: + raise ValueError("Argument '%s' is required." % arg) + + if 'PYTHON_BASE' not in self.env: + self.env['PYTHON_BASE'] = os.path.join(self.env['ROOT'], 'pythonenv') + + if 'PYTHON_BIN' not in self.env: + self.env['PYTHON_BIN'] = os.path.join( + self.env['PYTHON_BASE'], 'bin', 'python') + self.env['PYTHON_VERSION'] + + if 'PYTHON_SITE' not in self.env: + self.env['PYTHON_SITE'] = os.path.join( + self.env['PYTHON_BASE'], 'lib', + 'python' + self.env['PYTHON_VERSION'], 'site-packages') + + if 'APP_DIR' not in self.env: + self.env['APP_DIR'] = os.path.join(self.env['ROOT'], 'application') + + if 'CONFIG_DIR' not in self.env: + self.env['CONFIG_DIR'] = os.path.join(self.env['ROOT'], 'etc') + + if 'MEDIA_DIR' not in self.env: + self.env['MEDIA_DIR'] = os.path.join(self.env['ROOT'], 'www', 'media') + + self._logger = logging.getLogger("deployment") + + def info(self, *args, **kwargs): + self._logger.info(*args, **kwargs) + + def render_template(self, source, dest, extra_context={}): + self.info("Rendering template: %s", source) + + with open(source, 'rb') as source_file: + t = Template(source_file.read()) + + context = dict(self.env) + context.update(extra_context) + + with open(dest, 'wb') as dest_file: + dest_file.write(t.safe_substitute(context)) + + self.info("Done.") + + def restart_app(self): + pass + + def update_app(self): + pass + + def install_dependencies(self): + pass + + def deploy(self): + self.update_app() + self.install_dependencies() + self.update_config() + self.restart_app() + + def find_resource(self, path): + full_path = os.path.join(self.env['APP_DIR'], path) + if os.path.isfile(full_path): + return full_path + raise ValueError("Resource '%s' not found" % path) + + @classmethod + def run_deploy(cls, *args, **kwargs): + site = cls(*args, **kwargs) + return site.deploy() + +class WSGISite(DeploySite): + + def __init__(self, **env): + super(WSGISite, self).__init__(**env) + + if 'WSGI_FILE' not in self.env: + self.env['WSGI_FILE'] = os.path.join(self.env['ROOT'], 'www', + 'wsgi', self.env['PROJECT_NAME']) + '.wsgi' + + if 'WSGI_SOURCE_FILE' not in self.env: + self.env['WSGI_SOURCE_FILE'] = 'wsgi_app.template' -def restart_wsgi(): - print "Restarting wsgi application:", - os.system("touch %s" % WSGI_TARGET) - print "done." + if 'WSGI_USER' not in self.env: + self.env['WSGI_USER'] = 'www-data' -def update_application(): - print "Updating repository.", - os.system("cd %s; git pull" % PROJECT_ROOT) - print "Installing requirements" - os.system("pip install -r %s" % os.path.join(PROJECT_ROOT, 'requirements.txt')) - print "Installing local requirements" - os.system("pip install -r %s" % os.path.join(ROOT, 'etc', 'requirements.txt')) - print "done." + def restart_app(self): + self.info("Restarting wsgi application: %s", self.env['WSGI_FILE']) + os.system("touch %s" % self.env['WSGI_FILE']) + def update_config(self): + source = self.find_resource(self.env['WSGI_SOURCE_FILE']) + self.render_template(source, self.env['WSGI_FILE']) -PYTHON = os.path.join(ROOT, 'pythonenv', 'bin', 'python') -PYTHON_SITE = os.path.join(ROOT, 'pythonenv', 'lib', 'python2.6', 'site-packages') +class PIPSite(DeploySite): -PROJECT_NAME = 'redakcja' -PROJECT_ROOT = os.path.join(ROOT, 'application') + def install_dependencies(self): + self.info("Installing requirements") + os.system("pip install -r %s" % self.find_resource('requirements.txt')) -MEDIA_ROOT = os.path.join(ROOT, 'www', 'media') + try: + self.info("Installing local requirements") + os.system("pip install -r %s" % self.find_resource('requirements_local.txt')) + except ValueError: + pass -ADMIN_EMAIL = 'lrekucki@gmail.com' +class GitSite(DeploySite): -WSGI_TARGET = os.path.join(ROOT, 'www', 'wsgi', PROJECT_NAME + '.wsgi') -WSGI_DIR = os.path.dirname(WSGI_TARGET) + def update_app(self): + self.info("Updating repository.") + os.system("cd %s; git pull" % self.env['APP_DIR']) -WSGI_USER = PROJECT_NAME -WSGI_PROCESSES = 5 -WSGI_THREADS = 1 +class ApacheSite(DeploySite): -DOMAIN = 'redakcja.wolnelektury.pl' -DOMAIN_ALIASES = 'redakcja.nowoczesnapolska.org.pl' + def __init__(self, **env): + super(ApacheSite, self).__init__(**env) -# -# Load local configuration -# -sys.path = [ os.path.join(ROOT, 'etc') ] + sys.path + if 'VHOST_SOURCE_FILE' not in self.env: + self.env['VHOST_SOURCE_FILE'] = 'apache_vhost.template' -from local_deployment import * + if 'VHOST_FILE' not in self.env: + self.env['VHOST_FILE'] = os.path.join(self.env['CONFIG_DIR'], self.env['PROJECT_NAME'] + '.vhost') -if __name__ == '__main__': - update_application() - render_template(os.path.join(PROJECT_ROOT, PROJECT_NAME + '.wsgi.template'), WSGI_TARGET, context=globals()) - render_template(os.path.join(PROJECT_ROOT, PROJECT_NAME + '.vhost.template'), os.path.join(ROOT, 'etc', PROJECT_NAME + '.vhost'), context=globals()) - restart_wsgi() + def update_config(self): + source = self.find_resource(self.env['VHOST_SOURCE_FILE']) + self.render_template(source, self.env['VHOST_CONFIG_FILE'])