Commits

Benoit C committed 1a01758

first commit

  • Participants

Comments (0)

Files changed (4)

File confs/conf.yaml

+--- #YAML:1.1
+
+preprod:
+    distro:
+        name: debian
+        packages: [python-virtualenv, libmysqlclient15-dev]
+    project:
+        name: myproject
+        normal_user: foo
+        sudo_user: bar
+        logs: myproject.log
+    hosts:
+        web: [stage.example.com]
+    database:
+        type: mysql
+        host: localhost
+        port: 3306
+        user: myuser
+        db: mydatabase
+        pw: mypass
+    hg:
+        url: hg.example.com
+        repos: repos/
+        user: hguser
+        pw: hgpass
+    django:
+        doc_root: /path/to/root/project/
+        src_dir: /path/to/root/project/src/djangoproject/
+    virtualenv:
+        bin_dir: /path/to/root/sandbox/
+        
+    wsgi:
+        type: gunicorn
+        path: /path/to/root/project/sandbox/bin/
+        pidfile: /path/to/root/project/gunicorn.pid
+        logfile: /path/to/root/project/gunicorn.log
+        host: 127.0.0.1
+        port: [9111,9112]
+        workers: 2
+    httpd:
+        type: nginx
+        en: /etc/nginx/sites-enabled/
+        av: /etc/nginx/sites-available/
+        
+    services:
+        restart:
+            [memcached]
+        reload:
+            [nginx]
+
+prod:
+    distro:
+        name: debian
+        packages: [python-virtualenv, libmysqlclient15-dev]
+    project:
+        name: myproject
+        normal_user: foo
+        sudo_user: bar
+        logs: myproject.log
+    hosts:
+        web: [stage.example.com]
+    database:
+        type: mysql
+        host: localhost
+        port: 3306
+        user: myuser
+        db: mydatabase
+        pw: mypass
+    hg:
+        url: hg.example.com
+        repos: repos/
+        user: hguser
+        pw: hgpass
+    django:
+        doc_root: /path/to/root/project/
+        src_dir: /path/to/root/project/src/djangoproject/
+    virtualenv:
+        bin_dir: /path/to/root/sandbox/
+        
+    wsgi:
+        type: gunicorn
+        path: /path/to/root/project/sandbox/bin/
+        pidfile: /path/to/root/project/gunicorn.pid
+        logfile: /path/to/root/project/gunicorn.log
+        host: 127.0.0.1
+        port: [9111,9112]
+        workers: 2
+    httpd:
+        type: nginx
+        en: /etc/nginx/sites-enabled/
+        av: /etc/nginx/sites-available/
+        
+    services:
+        restart:
+            [memcached]
+        reload:
+            [nginx]
+
+
+

File src/decorators.py

+# -*- coding: latin-1 -*-
+"""
+
+
+
+"""
+import traceback
+
+
+from mexceptions import ImproperlyConfigured
+
+
+# how it sux !
+class Decorate(object):
+    def __init__(self):
+        pass
+        
+    def __call__(self, func):
+        pass
+
+
+class Permssions(Decorate):
+    def __init__(self, username, env):
+        self.username = username
+        self.env = env
+        
+    def __call__(self, func):
+        def _wrapper(*args, **kwargs):
+            try:
+                self.env.user = self.env.yaml.get('project').get(self.username)
+            except Exception, e:
+                raise ImproperlyConfigured("did you configured the 'project' part of your settings ?")
+            if not self.env.user:
+                raise ImproperlyConfigured("did you configured the 'project' part of your settings ?")
+            print func
+            return func(*args, **kwargs)
+
+        return _wrapper
+
+class ConfChecker(Decorate):
+    def __init__(self, conf, env):
+        self.conf = conf
+        self.env = env
+    
+    def __call__(self, func):
+        def _wrapper(*args, **kwargs):
+            conf = self.env.yaml
+            if not self.conf in conf:
+                raise ImproperlyConfigured("does %s exists in your configuration file ?" % self.conf)
+            return func(*args, **kwargs)
+
+        return _wrapper

File src/fabfile.py

+# -*- coding: utf-8 -
+"""
+    Une documentation de ce fabfile s'impose.
+
+    fabric est une solution de deploiment
+    -> http://docs.fabfile.org/0.9.0/
+
+    ce fichier définit des rules pour:
+
+    -> restarter les services (nginx, gunicorn, memcached)
+    -> mettre a jour le code (via mercurial)
+
+    chaque path est configurable via le fichier conf.yaml.
+
+    notez que:
+        - c'est super dépendant du path et du repository qui sont 
+        toujours organisés de la sorte chez moi:
+
+        repos/
+            src/
+                <project_django>/   # le dossier du projet django
+            sandbox/                # le virtualenv
+            deploy/                 # le dossier ou se trouve le fabfile
+            doc/                    # ce dossier est voué a etre vide
+
+    généralement, il me suffit de faire:
+    fab {pre,}prod install
+
+
+    note:
+        - pour gérer la DB, je ne fais que droper la DB (en preprod)
+    et resync la db.
+
+
+    todo: 
+        - ajouter south
+"""
+import os
+from fabric.api import *
+from fabric.contrib.files import exists, sed
+from decorators import Permssions, ConfChecker
+
+import yaml
+
+
+@Permssions("sudo_user", env)
+@ConfChecker("wsgi", env)
+def wsgi_restart():
+    conf = env.yaml.get("gunicorn")
+    django = env.yaml.get("django")
+    if exists(conf.get('pidfile')):
+        if isinstance(conf.get('port'), list):
+            for port in conf.get('port'):
+                pidfile = "%s.%d" % (conf.get('pidfile'), port)
+                run("kill -0 `cat %s`" % pidfile)
+                run("kill -HUP `head -1 %s`" % pidfile)
+        return
+
+    with cd("%s/src/inventeev" % env.path):
+        if isinstance(conf.get('port'), list):
+            for port in conf.get('port'):
+                run("%s/gunicorn_django --workers=%d --port=%d --pid=%s --log-file=%s -D" % \
+                        (conf.get('path'), conf.get('workers'),
+                         port, conf.get('pidfile'), conf.get('logfile')))
+        else:
+            run("%s/gunicorn_django --workers=%d --port=%d --pid=%s --log-file=%s" % \
+                    (conf.get('path'), conf.get('workers'),
+                     conf.get('port'), conf.get('pid'), conf.get('logfile')))
+
+
+def install_httpd_vhost():
+    print "installing nginx vhost for %s" % env.project_name
+    with cd(env.path):
+        conf = env.yaml.get("nginx")
+        fname = "%s/deploy/vhost-%s-%s" % (env.path, env.type, env.project_name)
+        run("mkdir -p %s/logs/" % env.path)
+        sed(fname, "DOCUMENT_ROOT", "%s" % env.path)
+        sudo("cp %s %s/%s" % (fname, conf.get("av"), env.project_name))
+        sudo("ln -sFf %s/%s %s" % (conf.get("av"), env.project_name, conf.get("en")))
+
+def preprod():
+    load_yaml('preprod')
+    env.project_name = env.yaml.get('project').get('name')
+    env.type = 'preprod'
+    env.hosts = env.yaml.get('hosts').get('web')
+    env.user = env.yaml.get('project').get('sudo_user')
+    env.path = '/home/%(user)s/preprod/%(project_name)s' % env
+    env.virtualhost_path = env.path
+
+def prod():
+    print "updating for %s => preprod" % env.project_name
+    load_yaml('prod')
+    env.type = 'prod'
+
+    env.hosts = env.yaml.get('hosts').get('web')
+    env.user = env.yaml.get('project').get('user')
+    env.path = '/home/%(user)s/django/%(project_name)s' % env
+    env.virtualhost_path = env.path
+
+@Permssions("normal_user", env)
+@ConfChecker("django", env)
+def django_settings():
+    conf = env.yaml.get('django')
+    with cd(env.path):
+        run("cp deploy/%s-settings.py %s/settings.py" % (env.type, conf.get('src_dir')))
+        sed("%s/settings.py" % conf.get('src_dir'), "DOCUMENT_ROOT", "%s" % env.path)
+
+
+@Permssions("normal_user", env)
+@ConfChecker("hg", env)
+def update_hg():
+    if remote_path_exist(env.path):
+        with cd(env.path):
+            run("hg pull -u")
+            run("hg update -C")
+    else:
+        conf = env.yaml.get("hg")
+        run("mkdir -p %s" % os.path.dirname(env.path))
+        run("hg clone http://%s:%s@%s/%s %s" % \
+                (conf.get('user'), conf.get('pw'), conf.get('url'),
+                 conf.get('repos'), env.path))
+
+@Permssions("normal_user", env)
+@ConfChecker("hg", env)
+def virtualenv():
+    if not exists(env.path + "/sandbox/"):
+        with cd(env.path):
+            run("virtualenv sandbox")
+            run("sandbox/bin/easy_install pip")
+    with cd(env.path):
+        run("sandbox/bin/pip install -r deploy/requirements.txt")
+
+@Permssions("sudo_user", env)
+@ConfChecker("database", env)
+def update_database():
+    conf = env.yaml.get("database")
+    #first, create a local change in the database with south
+    #apply the change
+
+
+@Permssions("sudo_user", env)
+@ConfChecker("services", env)
+def services_reload():
+    services = env.yaml.get('services').get('reload')
+    for service in services:
+        sudo("/etc/init.d/%s reload" % service)
+    print services
+
+@Permssions("sudo_user", env)
+@ConfChecker("services", env)
+def services_restart():
+    services = env.yaml.get('services').get('restart')
+    for service in services:
+        sudo("/etc/init.d/%s restart" % service)
+    print services
+
+    
+def load_yaml(env_type):
+    # sure we can do more there
+    env.yaml = yaml.load(file("conf.yaml").read()).get(env_type)
+
+@Permssions("sudo_user", env)
+@ConfChecker("distro", env)
+def distro_install():
+    # install packages specified in the yaml conf
+    dist_conf = env.yaml.get('distro')
+    if dist_conf.get('name') in ('ubuntu', 'debian'):
+        for package in dist_conf.get('packages'):
+            sudo("apt-get install %s" % packages)
+    
+
+def install():
+    """for the first install onto the machines"""
+    # install required 
+    
+    # update te hg repos
+    
+    # install_httpd_vhost
+    
+    # install django configuration
+    
+    # start/restart the wsgi/fcgi process
+    
+    # restart other services
+
+
+

File src/mexceptions.py

+"""
+    Some exceptions \o/
+"""
+
+class ImproperlyConfigured(Exception):
+    # from django ;)
+    "fabulator is somehow improperly configured"
+    pass