##// END OF EJS Templates
config: abort early if the environment doesn't allow Python to pass Unicode strings to the file system layer...
Mads Kiilerich -
r7299:0e33880b default
parent child Browse files
Show More
@@ -1,198 +1,214 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 Global configuration file for TurboGears2 specific settings in Kallithea.
15 Global configuration file for TurboGears2 specific settings in Kallithea.
16
16
17 This file complements the .ini file.
17 This file complements the .ini file.
18 """
18 """
19
19
20 import platform
20 import platform
21 import os, sys, logging
21 import os, sys, logging
22
22
23 import tg
23 import tg
24 from tg import hooks
24 from tg import hooks
25 from tg.configuration import AppConfig
25 from tg.configuration import AppConfig
26 from tg.support.converters import asbool
26 from tg.support.converters import asbool
27 import alembic.config
27 import alembic.config
28 from alembic.script.base import ScriptDirectory
28 from alembic.script.base import ScriptDirectory
29 from alembic.migration import MigrationContext
29 from alembic.migration import MigrationContext
30 from sqlalchemy import create_engine
30 from sqlalchemy import create_engine
31 import mercurial
31 import mercurial
32
32
33 from kallithea.lib.middleware.https_fixup import HttpsFixup
33 from kallithea.lib.middleware.https_fixup import HttpsFixup
34 from kallithea.lib.middleware.simplegit import SimpleGit
34 from kallithea.lib.middleware.simplegit import SimpleGit
35 from kallithea.lib.middleware.simplehg import SimpleHg
35 from kallithea.lib.middleware.simplehg import SimpleHg
36 from kallithea.lib.auth import set_available_permissions
36 from kallithea.lib.auth import set_available_permissions
37 from kallithea.lib.utils import load_rcextensions, make_ui, set_app_settings, set_vcs_config, \
37 from kallithea.lib.utils import load_rcextensions, make_ui, set_app_settings, set_vcs_config, \
38 set_indexer_config, check_git_version, repo2db_mapper
38 set_indexer_config, check_git_version, repo2db_mapper
39 from kallithea.lib.utils2 import str2bool
39 from kallithea.lib.utils2 import str2bool
40 import kallithea.model.base
40 import kallithea.model.base
41 from kallithea.model.scm import ScmModel
41 from kallithea.model.scm import ScmModel
42
42
43 import formencode
43 import formencode
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class KallitheaAppConfig(AppConfig):
48 class KallitheaAppConfig(AppConfig):
49 # Note: AppConfig has a misleading name, as it's not the application
49 # Note: AppConfig has a misleading name, as it's not the application
50 # configuration, but the application configurator. The AppConfig values are
50 # configuration, but the application configurator. The AppConfig values are
51 # used as a template to create the actual configuration, which might
51 # used as a template to create the actual configuration, which might
52 # overwrite or extend the one provided by the configurator template.
52 # overwrite or extend the one provided by the configurator template.
53
53
54 # To make it clear, AppConfig creates the config and sets into it the same
54 # To make it clear, AppConfig creates the config and sets into it the same
55 # values that AppConfig itself has. Then the values from the config file and
55 # values that AppConfig itself has. Then the values from the config file and
56 # gearbox options are loaded and merged into the configuration. Then an
56 # gearbox options are loaded and merged into the configuration. Then an
57 # after_init_config(conf) method of AppConfig is called for any change that
57 # after_init_config(conf) method of AppConfig is called for any change that
58 # might depend on options provided by configuration files.
58 # might depend on options provided by configuration files.
59
59
60 def __init__(self):
60 def __init__(self):
61 super(KallitheaAppConfig, self).__init__()
61 super(KallitheaAppConfig, self).__init__()
62
62
63 self['package'] = kallithea
63 self['package'] = kallithea
64
64
65 self['prefer_toscawidgets2'] = False
65 self['prefer_toscawidgets2'] = False
66 self['use_toscawidgets'] = False
66 self['use_toscawidgets'] = False
67
67
68 self['renderers'] = []
68 self['renderers'] = []
69
69
70 # Enable json in expose
70 # Enable json in expose
71 self['renderers'].append('json')
71 self['renderers'].append('json')
72
72
73 # Configure template rendering
73 # Configure template rendering
74 self['renderers'].append('mako')
74 self['renderers'].append('mako')
75 self['default_renderer'] = 'mako'
75 self['default_renderer'] = 'mako'
76 self['use_dotted_templatenames'] = False
76 self['use_dotted_templatenames'] = False
77
77
78 # Configure Sessions, store data as JSON to avoid pickle security issues
78 # Configure Sessions, store data as JSON to avoid pickle security issues
79 self['session.enabled'] = True
79 self['session.enabled'] = True
80 self['session.data_serializer'] = 'json'
80 self['session.data_serializer'] = 'json'
81
81
82 # Configure the base SQLALchemy Setup
82 # Configure the base SQLALchemy Setup
83 self['use_sqlalchemy'] = True
83 self['use_sqlalchemy'] = True
84 self['model'] = kallithea.model.base
84 self['model'] = kallithea.model.base
85 self['DBSession'] = kallithea.model.meta.Session
85 self['DBSession'] = kallithea.model.meta.Session
86
86
87 # Configure App without an authentication backend.
87 # Configure App without an authentication backend.
88 self['auth_backend'] = None
88 self['auth_backend'] = None
89
89
90 # Use custom error page for these errors. By default, Turbogears2 does not add
90 # Use custom error page for these errors. By default, Turbogears2 does not add
91 # 400 in this list.
91 # 400 in this list.
92 # Explicitly listing all is considered more robust than appending to defaults,
92 # Explicitly listing all is considered more robust than appending to defaults,
93 # in light of possible future framework changes.
93 # in light of possible future framework changes.
94 self['errorpage.status_codes'] = [400, 401, 403, 404]
94 self['errorpage.status_codes'] = [400, 401, 403, 404]
95
95
96 # Disable transaction manager -- currently Kallithea takes care of transactions itself
96 # Disable transaction manager -- currently Kallithea takes care of transactions itself
97 self['tm.enabled'] = False
97 self['tm.enabled'] = False
98
98
99
99
100 base_config = KallitheaAppConfig()
100 base_config = KallitheaAppConfig()
101
101
102 # TODO still needed as long as we use pylonslib
102 # TODO still needed as long as we use pylonslib
103 sys.modules['pylons'] = tg
103 sys.modules['pylons'] = tg
104
104
105 # DebugBar, a debug toolbar for TurboGears2.
105 # DebugBar, a debug toolbar for TurboGears2.
106 # (https://github.com/TurboGears/tgext.debugbar)
106 # (https://github.com/TurboGears/tgext.debugbar)
107 # To enable it, install 'tgext.debugbar' and 'kajiki', and run Kallithea with
107 # To enable it, install 'tgext.debugbar' and 'kajiki', and run Kallithea with
108 # 'debug = true' (not in production!)
108 # 'debug = true' (not in production!)
109 # See the Kallithea documentation for more information.
109 # See the Kallithea documentation for more information.
110 try:
110 try:
111 from tgext.debugbar import enable_debugbar
111 from tgext.debugbar import enable_debugbar
112 import kajiki # only to check its existence
112 import kajiki # only to check its existence
113 except ImportError:
113 except ImportError:
114 pass
114 pass
115 else:
115 else:
116 base_config['renderers'].append('kajiki')
116 base_config['renderers'].append('kajiki')
117 enable_debugbar(base_config)
117 enable_debugbar(base_config)
118
118
119
119
120 def setup_configuration(app):
120 def setup_configuration(app):
121 config = app.config
121 config = app.config
122
122
123 # Verify that things work when Dulwich passes unicode paths to the file system layer.
124 # Note: UTF-8 is preferred, but for example ISO-8859-1 or mbcs should also work under the right cirumstances.
125 try:
126 u'\xe9'.encode(sys.getfilesystemencoding()) # Test using Γ© (&eacute;)
127 except UnicodeEncodeError:
128 log.error("Cannot encode Unicode paths to file system encoding %r", sys.getfilesystemencoding())
129 for var in ['LC_CTYPE', 'LC_ALL', 'LANG']:
130 if var in os.environ:
131 val = os.environ[var]
132 log.error("Note: Environment variable %s is %r - perhaps change it to some other value from 'locale -a', like 'C.UTF-8' or 'en_US.UTF-8'", var, val)
133 break
134 else:
135 log.error("Note: No locale setting found in environment variables - perhaps set LC_CTYPE to some value from 'locale -a', like 'C.UTF-8' or 'en_US.UTF-8'")
136 log.error("Terminating ...")
137 sys.exit(1)
138
123 # Mercurial sets encoding at module import time, so we have to monkey patch it
139 # Mercurial sets encoding at module import time, so we have to monkey patch it
124 hgencoding = config.get('hgencoding')
140 hgencoding = config.get('hgencoding')
125 if hgencoding:
141 if hgencoding:
126 mercurial.encoding.encoding = hgencoding
142 mercurial.encoding.encoding = hgencoding
127
143
128 if config.get('ignore_alembic_revision', False):
144 if config.get('ignore_alembic_revision', False):
129 log.warn('database alembic revision checking is disabled')
145 log.warn('database alembic revision checking is disabled')
130 else:
146 else:
131 dbconf = config['sqlalchemy.url']
147 dbconf = config['sqlalchemy.url']
132 alembic_cfg = alembic.config.Config()
148 alembic_cfg = alembic.config.Config()
133 alembic_cfg.set_main_option('script_location', 'kallithea:alembic')
149 alembic_cfg.set_main_option('script_location', 'kallithea:alembic')
134 alembic_cfg.set_main_option('sqlalchemy.url', dbconf)
150 alembic_cfg.set_main_option('sqlalchemy.url', dbconf)
135 script_dir = ScriptDirectory.from_config(alembic_cfg)
151 script_dir = ScriptDirectory.from_config(alembic_cfg)
136 available_heads = sorted(script_dir.get_heads())
152 available_heads = sorted(script_dir.get_heads())
137
153
138 engine = create_engine(dbconf)
154 engine = create_engine(dbconf)
139 with engine.connect() as conn:
155 with engine.connect() as conn:
140 context = MigrationContext.configure(conn)
156 context = MigrationContext.configure(conn)
141 current_heads = sorted(str(s) for s in context.get_current_heads())
157 current_heads = sorted(str(s) for s in context.get_current_heads())
142 if current_heads != available_heads:
158 if current_heads != available_heads:
143 log.error('Failed to run Kallithea:\n\n'
159 log.error('Failed to run Kallithea:\n\n'
144 'The database version does not match the Kallithea version.\n'
160 'The database version does not match the Kallithea version.\n'
145 'Please read the documentation on how to upgrade or downgrade the database.\n'
161 'Please read the documentation on how to upgrade or downgrade the database.\n'
146 'Current database version id(s): %s\n'
162 'Current database version id(s): %s\n'
147 'Expected database version id(s): %s\n'
163 'Expected database version id(s): %s\n'
148 'If you are a developer and you know what you are doing, you can add `ignore_alembic_revision = True` '
164 'If you are a developer and you know what you are doing, you can add `ignore_alembic_revision = True` '
149 'to your .ini file to skip the check.\n' % (' '.join(current_heads), ' '.join(available_heads)))
165 'to your .ini file to skip the check.\n' % (' '.join(current_heads), ' '.join(available_heads)))
150 sys.exit(1)
166 sys.exit(1)
151
167
152 # store some globals into kallithea
168 # store some globals into kallithea
153 kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
169 kallithea.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
154 kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager'))
170 kallithea.CELERY_EAGER = str2bool(config['app_conf'].get('celery.always.eager'))
155 kallithea.CONFIG = config
171 kallithea.CONFIG = config
156
172
157 load_rcextensions(root_path=config['here'])
173 load_rcextensions(root_path=config['here'])
158
174
159 set_available_permissions(config)
175 set_available_permissions(config)
160 repos_path = make_ui('db').configitems('paths')[0][1]
176 repos_path = make_ui('db').configitems('paths')[0][1]
161 config['base_path'] = repos_path
177 config['base_path'] = repos_path
162 set_app_settings(config)
178 set_app_settings(config)
163
179
164 instance_id = kallithea.CONFIG.get('instance_id', '*')
180 instance_id = kallithea.CONFIG.get('instance_id', '*')
165 if instance_id == '*':
181 if instance_id == '*':
166 instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
182 instance_id = '%s-%s' % (platform.uname()[1], os.getpid())
167 kallithea.CONFIG['instance_id'] = instance_id
183 kallithea.CONFIG['instance_id'] = instance_id
168
184
169 # update kallithea.CONFIG with the meanwhile changed 'config'
185 # update kallithea.CONFIG with the meanwhile changed 'config'
170 kallithea.CONFIG.update(config)
186 kallithea.CONFIG.update(config)
171
187
172 # configure vcs and indexer libraries (they are supposed to be independent
188 # configure vcs and indexer libraries (they are supposed to be independent
173 # as much as possible and thus avoid importing tg.config or
189 # as much as possible and thus avoid importing tg.config or
174 # kallithea.CONFIG).
190 # kallithea.CONFIG).
175 set_vcs_config(kallithea.CONFIG)
191 set_vcs_config(kallithea.CONFIG)
176 set_indexer_config(kallithea.CONFIG)
192 set_indexer_config(kallithea.CONFIG)
177
193
178 check_git_version()
194 check_git_version()
179
195
180
196
181 hooks.register('configure_new_app', setup_configuration)
197 hooks.register('configure_new_app', setup_configuration)
182
198
183
199
184 def setup_application(app):
200 def setup_application(app):
185 config = app.config
201 config = app.config
186
202
187 # we want our low level middleware to get to the request ASAP. We don't
203 # we want our low level middleware to get to the request ASAP. We don't
188 # need any stack middleware in them - especially no StatusCodeRedirect buffering
204 # need any stack middleware in them - especially no StatusCodeRedirect buffering
189 app = SimpleHg(app, config)
205 app = SimpleHg(app, config)
190 app = SimpleGit(app, config)
206 app = SimpleGit(app, config)
191
207
192 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
208 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
193 if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
209 if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
194 app = HttpsFixup(app, config)
210 app = HttpsFixup(app, config)
195 return app
211 return app
196
212
197
213
198 hooks.register('before_config', setup_application)
214 hooks.register('before_config', setup_application)
General Comments 0
You need to be logged in to leave comments. Login now