Changeset - 1ef52a70f3b7
[Not reviewed]
default
1 9 0
Marcin Kuzminski - 15 years ago 2010-07-14 02:28:32
marcin@python-works.com
Made config file free configuration based on database and capable of beeing manage via application settings + some code cleanups
10 files changed with 132 insertions and 89 deletions:
0 comments (0 inline, 0 general)
pylons_app/config/environment.py
Show inline comments
 
"""Pylons environment configuration"""
 
from mako.lookup import TemplateLookup
 
from pylons.configuration import PylonsConfig
 
from pylons.error import handle_mako_error
 
from pylons_app.config.routing import make_map
 
from pylons_app.lib.auth import set_available_permissions, set_base_path
 
from pylons_app.lib.utils import repo2db_mapper
 
from pylons_app.lib.utils import repo2db_mapper, make_ui, set_hg_app_config
 
from pylons_app.model import init_model
 
from pylons_app.model.hg_model import _get_repos_cached_initial
 
from sqlalchemy import engine_from_config
 
import logging
 
import os
 
import pylons_app.lib.app_globals as app_globals
 
@@ -58,13 +58,16 @@ def load_environment(global_conf, app_co
 
        sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
 
                                                            proxy=TimerProxy())
 
    else:
 
        sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
 

	
 
    init_model(sa_engine_db1)
 
    config['pylons.app_globals'].baseui = make_ui('db')
 
    
 
    repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals']))
 
    set_available_permissions(config)
 
    set_base_path(config)
 
    set_hg_app_config(config)
 
    # CONFIGURATION OPTIONS HERE (note: all config options will override
 
    # any Pylons config options)
 
    
 
    return config
pylons_app/config/middleware.py
Show inline comments
 
@@ -38,13 +38,12 @@ def make_app(global_conf, full_stack=Tru
 
    
 
    # Routing/Session/Cache Middleware
 
    app = RoutesMiddleware(app, config['routes.map'])
 
    app = SessionMiddleware(app, config)
 
    
 
    # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
 
    #set the https based on HTTP_X_URL_SCHEME
 
    
 
    app = SimpleHg(app, config)
 
    
 
    if asbool(full_stack):
 
        # Handle Python exceptions
 
        app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
pylons_app/config/repositories.config_tmpl
Show inline comments
 
deleted file
pylons_app/controllers/settings.py
Show inline comments
 
@@ -67,14 +67,12 @@ class SettingsController(BaseController)
 
            defaults=defaults,
 
            encoding="UTF-8",
 
            force_defaults=False
 
        )  
 

	
 
    def update(self, repo_name):
 
        print request.POST
 
        print 'x' * 110
 
        repo_model = RepoModel()
 
        _form = RepoSettingsForm(edit=True)()
 
        try:
 
            form_result = _form.to_python(dict(request.POST))
 
            repo_model.update(repo_name, form_result)
 
            invalidate_cache('cached_repo_list')
pylons_app/lib/app_globals.py
Show inline comments
 
"""The application's Globals object"""
 

	
 
from beaker.cache import CacheManager
 
from beaker.util import parse_cache_config_options
 
from pylons_app.lib.utils import make_ui
 
from vcs.utils.lazy import LazyProperty
 

	
 
class Globals(object):
 

	
 
    """Globals acts as a container for objects available throughout the
 
    life of the application
 

	
 
    """
 

	
 
    def __init__(self, config):
 
        """One instance of Globals is created during application
 
        initialization and is available during requests via the
 
        'app_globals' variable
 

	
 
        """
 
        self.cache = CacheManager(**parse_cache_config_options(config))
 
        self.baseui = make_ui(config['hg_app_repo_conf'])
 
        self.paths = self.baseui.configitems('paths')
 
        self.base_path = self.paths[0][1].replace('*', '')
 
        self.changeset_annotation_colors = {}
 
        self.available_permissions = None # propagated after init_model
 
        self.app_title = None               # propagated after init_model
 
        self.baseui = None                  # propagated after init_model        
 
        
 
    @LazyProperty
 
    def paths(self):
 
        if self.baseui:
 
            return self.baseui.configitems('paths')
 
    
 
    @LazyProperty
 
    def base_path(self):
 
        if self.baseui:
 
            return self.paths[0][1].replace('*', '')            
pylons_app/lib/db_manage.py
Show inline comments
 
@@ -30,13 +30,13 @@ import sys
 
import uuid
 
ROOT = dn(dn(dn(os.path.realpath(__file__))))
 
sys.path.append(ROOT)
 

	
 
from pylons_app.lib.auth import get_crypt_password
 
from pylons_app.model import init_model
 
from pylons_app.model.db import User, Permission
 
from pylons_app.model.db import User, Permission, HgAppUi
 
from pylons_app.model.meta import Session, Base
 
from sqlalchemy.engine import create_engine
 
import logging
 

	
 
log = logging.getLogger('db manage')
 
log.setLevel(logging.DEBUG)
 
@@ -77,12 +77,67 @@ class DbManage(object):
 
    def admin_prompt(self):
 
        import getpass
 
        username = raw_input('Specify admin username:')
 
        password = getpass.getpass('Specify admin password:')
 
        self.create_user(username, password, True)
 
        
 
    def config_prompt(self):
 
        log.info('Seting up repositories.config')
 
        
 
        
 
        path = raw_input('Specify valid full path to your repositories'
 
                        ' you can change this later application settings:')
 
        
 
        if not os.path.isdir(path):
 
            log.error('You entered wrong path')
 
            sys.exit()
 
        
 
        hooks = HgAppUi()
 
        hooks.ui_section = 'hooks'
 
        hooks.ui_key = 'changegroup'
 
        hooks.ui_value = 'hg update >&2'
 
        
 
        web1 = HgAppUi()
 
        web1.ui_section = 'web'
 
        web1.ui_key = 'push_ssl'
 
        web1.ui_value = 'false'
 
                
 
        web2 = HgAppUi()
 
        web2.ui_section = 'web'
 
        web2.ui_key = 'allow_archive'
 
        web2.ui_value = 'gz zip bz2'
 
                
 
        web3 = HgAppUi()
 
        web3.ui_section = 'web'
 
        web3.ui_key = 'allow_push'
 
        web3.ui_value = '*'
 
        
 
        web4 = HgAppUi()
 
        web4.ui_section = 'web'
 
        web4.ui_key = 'baseurl'
 
        web4.ui_value = '/'                        
 
        
 
        paths = HgAppUi()
 
        paths.ui_section = 'paths'
 
        paths.ui_key = '/'
 
        paths.ui_value = os.path.join(path, '*')
 
        
 
        
 
        try:
 
            self.sa.add(hooks)
 
            self.sa.add(web1)
 
            self.sa.add(web2)
 
            self.sa.add(web3)
 
            self.sa.add(web4)
 
            self.sa.add(paths)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise        
 
        log.info('created ui config')
 
                    
 
    def create_user(self, username, password, admin=False):
 
        
 
        log.info('creating default user')
 
        #create default user for handling default permissions.
 
        def_user = User()
 
        def_user.username = 'default'
 
@@ -90,25 +145,24 @@ class DbManage(object):
 
        def_user.name = 'default'
 
        def_user.lastname = 'default'
 
        def_user.email = 'default@default.com'
 
        def_user.admin = False
 
        def_user.active = False
 
        
 
        self.sa.add(def_user)
 
        
 
        log.info('creating administrator user %s', username)
 
        new_user = User()
 
        new_user.username = username
 
        new_user.password = get_crypt_password(password)
 
        new_user.name = 'Hg'
 
        new_user.lastname = 'Admin'
 
        new_user.email = 'admin@localhost'
 
        new_user.admin = admin
 
        new_user.active = True
 
        
 
        try:
 
            self.sa.add(def_user)
 
            self.sa.add(new_user)
 
            self.sa.commit()
 
        except:
 
            self.sa.rollback()
 
            raise
 
    
pylons_app/lib/middleware/simplehg.py
Show inline comments
 
@@ -47,13 +47,13 @@ log = logging.getLogger(__name__)
 
class SimpleHg(object):
 

	
 
    def __init__(self, application, config):
 
        self.application = application
 
        self.config = config
 
        #authenticate this mercurial request using 
 
        realm = '%s %s' % (self.config['hg_app_name'], 'mercurial repository')
 
        realm = self.config['hg_app_auth_realm']
 
        self.authenticate = AuthBasicAuthenticator(realm, authfunc)
 
        
 
    def __call__(self, environ, start_response):
 
        if not is_mercurial(environ):
 
            return self.application(environ, start_response)
 
        else:
 
@@ -108,20 +108,19 @@ class SimpleHg(object):
 
                self.__log_user_action(user, action, repo_name, ipaddr)            
 
            
 
            #===================================================================
 
            # MERCURIAL REQUEST HANDLING
 
            #===================================================================
 
            environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
 
            self.baseui = make_ui(self.config['hg_app_repo_conf'])
 
            self.baseui = make_ui('db')
 
            self.basepath = self.config['base_path']
 
            self.repo_path = os.path.join(self.basepath, repo_name)
 

	
 
            #quick check if that dir exists...
 
            if check_repo_fast(repo_name, self.basepath):
 
                return HTTPNotFound()(environ, start_response)
 
            
 
            try:
 
                app = wsgiapplication(self.__make_app)
 
            except RepoError as e:
 
                if str(e).find('not found') != -1:
 
                    return HTTPNotFound()(environ, start_response)
 
            except Exception:
 
@@ -152,13 +151,13 @@ class SimpleHg(object):
 
            for msg in msg_list:
 
                yield msg + '\n'
 
        org_response = app(environ, start_response)
 
        return chain(org_response, custom_messages(messages))
 

	
 
    def __make_app(self):
 
        hgserve = hgweb(self.repo_path)
 
        hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
 
        return  self.__load_web_settings(hgserve)
 
    
 
    def __get_environ_user(self, environ):
 
        return environ.get('REMOTE_USER')
 
    
 
    def __get_size(self, repo_path, content_size):
 
@@ -211,15 +210,17 @@ class SimpleHg(object):
 
        push requests"""
 
        invalidate_cache('cached_repo_list')
 
        invalidate_cache('full_changelog', repo_name)
 
           
 
                   
 
    def __load_web_settings(self, hgserve):
 
        repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False)
 
        #set the global ui for hgserve
 
        hgserve.repo.ui = self.baseui
 
        
 
        hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
 
        repoui = make_ui('file', hgrc, False)
 
        
 
        if repoui:
 
            #set the repository based config
 
            hgserve.repo.ui = repoui
 
            
 
        return hgserve
pylons_app/lib/utils.py
Show inline comments
 
@@ -24,13 +24,13 @@ Utilities for hg app
 
"""
 

	
 
import os
 
import logging
 
from mercurial import ui, config, hg
 
from mercurial.error import RepoError
 
from pylons_app.model.db import Repository, User
 
from pylons_app.model.db import Repository, User, HgAppUi
 
log = logging.getLogger(__name__)
 

	
 

	
 
def get_repo_slug(request):
 
    return request.environ['pylons.routes_dict'].get('repo_name')
 

	
 
@@ -72,59 +72,64 @@ def check_repo(repo_name, base_path, ver
 
        return False
 
    except RepoError:
 
        #it means that there is no valid repo there...
 
        log.info('%s repo is free for creation', repo_name)
 
        return True
 

	
 
def make_ui(path=None, checkpaths=True):        
 
def make_ui(read_from='file', path=None, checkpaths=True):        
 
    """
 
    A funcion that will read python rc files and make an ui from read options
 
    A function that will read python rc files or database
 
    and make an mercurial ui object from read options
 
    
 
    @param path: path to mercurial config file
 
    @param checkpaths: check the path
 
    @param read_from: read from 'file' or 'db'
 
    """
 
    if not path:
 
        log.error('repos config path is empty !')
 
    #propagated from mercurial documentation
 
    sections = ['alias', 'auth',
 
                'decode/encode', 'defaults',
 
                'diff', 'email',
 
                'extensions', 'format',
 
                'merge-patterns', 'merge-tools',
 
                'hooks', 'http_proxy',
 
                'smtp', 'patch',
 
                'paths', 'profiling',
 
                'server', 'trusted',
 
                'ui', 'web', ]
 
    baseui = ui.ui()
 
    
 
                
 
    if read_from == 'file':
 
    if not os.path.isfile(path):
 
        log.warning('Unable to read config file %s' % path)
 
        return False
 
    #propagated from mercurial documentation
 
    sections = [
 
                'alias',
 
                'auth',
 
                'decode/encode',
 
                'defaults',
 
                'diff',
 
                'email',
 
                'extensions',
 
                'format',
 
                'merge-patterns',
 
                'merge-tools',
 
                'hooks',
 
                'http_proxy',
 
                'smtp',
 
                'patch',
 
                'paths',
 
                'profiling',
 
                'server',
 
                'trusted',
 
                'ui',
 
                'web',
 
                ]
 

	
 
    baseui = ui.ui()
 
    cfg = config.config()
 
    cfg.read(path)
 
    if checkpaths:check_repo_dir(cfg.items('paths'))
 

	
 
    for section in sections:
 
        for k, v in cfg.items(section):
 
            baseui.setconfig(section, k, v)
 
        if checkpaths:check_repo_dir(cfg.items('paths'))                
 
              
 
        
 
    elif read_from == 'db':
 
        from pylons_app.model.meta import Session
 
        sa = Session()
 
            
 
        hg_ui = sa.query(HgAppUi).all()
 
        for ui_ in hg_ui:
 
            baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
 
        
 
    
 
    return baseui
 

	
 

	
 
def set_hg_app_config(config):
 
    config['hg_app_auth_realm'] = 'realm'
 
    config['hg_app_name'] = 'app name'
 

	
 
def invalidate_cache(name, *args):
 
    """Invalidates given name cache"""
 
    
 
    from beaker.cache import region_invalidate
 
    log.info('INVALIDATING CACHE FOR %s', name)
 
    
pylons_app/model/db.py
Show inline comments
 
from pylons_app.model.meta import Base
 
from sqlalchemy.orm import relation, backref
 
from sqlalchemy import *
 
from vcs.utils.lazy import LazyProperty
 

	
 
class HgAppSettings(Base):
 
    __tablename__ = 'hg_app_settings'
 
    __table_args__ = {'useexisting':True}
 
    app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    app_title = Column("app_title", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    app_auth_realm = Column("auth_realm", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
class HgAppUi(Base):
 
    __tablename__ = 'hg_app_ui'
 
    __table_args__ = {'useexisting':True}
 
    ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 

	
 
class User(Base): 
 
    __tablename__ = 'users'
 
    __table_args__ = {'useexisting':True}
 
    user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
 
    username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
 
    password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
pylons_app/websetup.py
Show inline comments
 
@@ -9,41 +9,15 @@ import sys
 

	
 
log = logging.getLogger(__name__)
 

	
 
ROOT = dn(dn(os.path.realpath(__file__)))
 
sys.path.append(ROOT)
 

	
 

	
 
def setup_repository():
 
    log.info('Seting up repositories.config')
 
    fname = 'repositories.config'
 
    
 
    try:
 
        tmpl = open(jn(ROOT, 'pylons_app', 'config', 'repositories.config_tmpl')).read()
 
    except IOError:
 
        raise
 
    
 
    path = raw_input('Specify valid full path to your repositories'
 
                    ' you can change this later in repositories.config file:')
 
    
 
    if not os.path.isdir(path):
 
        log.error('You entered wrong path')
 
        sys.exit()
 
    
 
    
 
    path = jn(path, '*') 
 
    dest_path = jn(ROOT, fname)
 
    f = open(dest_path, 'wb')
 
    f.write(tmpl % {'repo_location':path})
 
    f.close()
 
    log.info('created repositories.config in %s', dest_path)
 
        
 

	
 
def setup_app(command, conf, vars):
 
    """Place any commands to setup pylons_app here"""
 
    setup_repository()
 
    dbmanage = DbManage(log_sql=True)
 
    dbmanage.create_tables(override=True)
 
    dbmanage.config_prompt()
 
    dbmanage.admin_prompt()
 
    dbmanage.create_permissions()
 
    load_environment(conf.global_conf, conf.local_conf)
 

	
0 comments (0 inline, 0 general)