##// END OF EJS Templates
db: Move db setup code to seperate function.
johbo -
r121:6b332d07 default
parent child Browse files
Show More
@@ -1,176 +1,192 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Pylons environment configuration
23 23 """
24 24
25 25 import os
26 26 import logging
27 27 import rhodecode
28 28 import platform
29 29 import re
30 30 import io
31 31
32 32 from mako.lookup import TemplateLookup
33 33 from pylons.configuration import PylonsConfig
34 34 from pylons.error import handle_mako_error
35 35 from pyramid.settings import asbool
36 36
37 37 # don't remove this import it does magic for celery
38 38 from rhodecode.lib import celerypylons # noqa
39 39
40 40 import rhodecode.lib.app_globals as app_globals
41 41
42 42 from rhodecode.config import utils
43 43 from rhodecode.config.routing import make_map
44 44 from rhodecode.config.jsroutes import generate_jsroutes_content
45 45
46 46 from rhodecode.lib import helpers
47 47 from rhodecode.lib.auth import set_available_permissions
48 48 from rhodecode.lib.utils import (
49 49 repo2db_mapper, make_db_config, set_rhodecode_config,
50 50 load_rcextensions)
51 51 from rhodecode.lib.utils2 import str2bool, aslist
52 52 from rhodecode.lib.vcs import connect_vcs, start_vcs_server
53 53 from rhodecode.model.scm import ScmModel
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57 def load_environment(global_conf, app_conf, initial=False,
58 58 test_env=None, test_index=None):
59 59 """
60 60 Configure the Pylons environment via the ``pylons.config``
61 61 object
62 62 """
63 63 config = PylonsConfig()
64 64
65 65
66 66 # Pylons paths
67 67 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
68 68 paths = {
69 69 'root': root,
70 70 'controllers': os.path.join(root, 'controllers'),
71 71 'static_files': os.path.join(root, 'public'),
72 72 'templates': [os.path.join(root, 'templates')],
73 73 }
74 74
75 75 # Initialize config with the basic options
76 76 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
77 77
78 78 # store some globals into rhodecode
79 79 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
80 80 rhodecode.CELERY_EAGER = str2bool(
81 81 config['app_conf'].get('celery.always.eager'))
82 82
83 83 config['routes.map'] = make_map(config)
84 84
85 85 if asbool(config['debug']):
86 86 jsroutes = config['routes.map'].jsroutes()
87 87 jsroutes_file_content = generate_jsroutes_content(jsroutes)
88 88 jsroutes_file_path = os.path.join(
89 89 paths['static_files'], 'js', 'rhodecode', 'routes.js')
90 90
91 91 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
92 92 f.write(jsroutes_file_content)
93 93
94 94 config['pylons.app_globals'] = app_globals.Globals(config)
95 95 config['pylons.h'] = helpers
96 96 rhodecode.CONFIG = config
97 97
98 98 load_rcextensions(root_path=config['here'])
99 99
100 100 # Setup cache object as early as possible
101 101 import pylons
102 102 pylons.cache._push_object(config['pylons.app_globals'].cache)
103 103
104 104 # Create the Mako TemplateLookup, with the default auto-escaping
105 105 config['pylons.app_globals'].mako_lookup = TemplateLookup(
106 106 directories=paths['templates'],
107 107 error_handler=handle_mako_error,
108 108 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
109 109 input_encoding='utf-8', default_filters=['escape'],
110 110 imports=['from webhelpers.html import escape'])
111 111
112 112 # sets the c attribute access when don't existing attribute are accessed
113 113 config['pylons.strict_tmpl_context'] = True
114 114
115 115 # Limit backends to "vcs.backends" from configuration
116 116 backends = config['vcs.backends'] = aslist(
117 117 config.get('vcs.backends', 'hg,git'), sep=',')
118 118 for alias in rhodecode.BACKENDS.keys():
119 119 if alias not in backends:
120 120 del rhodecode.BACKENDS[alias]
121 121 log.info("Enabled backends: %s", backends)
122 122
123 123 # initialize vcs client and optionally run the server if enabled
124 124 vcs_server_uri = config.get('vcs.server', '')
125 125 vcs_server_enabled = str2bool(config.get('vcs.server.enable', 'true'))
126 126 start_server = (
127 127 str2bool(config.get('vcs.start_server', 'false')) and
128 128 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
129 129 if vcs_server_enabled and start_server:
130 130 log.info("Starting vcsserver")
131 131 start_vcs_server(server_and_port=vcs_server_uri,
132 132 protocol=utils.get_vcs_server_protocol(config),
133 133 log_level=config['vcs.server.log_level'])
134 134
135 135 set_available_permissions(config)
136 136 db_cfg = make_db_config(clear_session=True)
137 137
138 138 repos_path = list(db_cfg.items('paths'))[0][1]
139 139 config['base_path'] = repos_path
140 140
141 141 config['vcs.hooks.direct_calls'] = _use_direct_hook_calls(config)
142 142 config['vcs.hooks.protocol'] = _get_vcs_hooks_protocol(config)
143 143
144 144 # store db config also in main global CONFIG
145 145 set_rhodecode_config(config)
146 146
147 147 # configure instance id
148 148 utils.set_instance_id(config)
149 149
150 150 # CONFIGURATION OPTIONS HERE (note: all config options will override
151 151 # any Pylons config options)
152 152
153 153 # store config reference into our module to skip import magic of pylons
154 154 rhodecode.CONFIG.update(config)
155 155
156 156 utils.configure_pyro4(config)
157 157 utils.configure_vcs(config)
158 158 if vcs_server_enabled:
159 159 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(config))
160 160
161 161 import_on_startup = str2bool(config.get('startup.import_repos', False))
162 162 if vcs_server_enabled and import_on_startup:
163 163 repo2db_mapper(ScmModel().repo_scan(repos_path), remove_obsolete=False)
164 164 return config
165 165
166 166
167 167 def _use_direct_hook_calls(config):
168 168 default_direct_hook_calls = 'false'
169 169 direct_hook_calls = str2bool(
170 170 config.get('vcs.hooks.direct_calls', default_direct_hook_calls))
171 171 return direct_hook_calls
172 172
173 173
174 174 def _get_vcs_hooks_protocol(config):
175 175 protocol = config.get('vcs.hooks.protocol', 'pyro4').lower()
176 176 return protocol
177
178
179 def load_pyramid_environment(global_config, settings):
180 # Some parts of the code expect a merge of global and app settings.
181 settings_merged = global_config.copy()
182 settings_merged.update(settings)
183
184 # If this is a test run we prepare the test environment like
185 # creating a test database, test search index and test repositories.
186 # This has to be done before the database connection is initialized.
187 if settings['is_test']:
188 rhodecode.is_test = True
189 utils.initialize_test_environment(settings_merged)
190
191 # Initialize the database connection.
192 utils.initialize_database(settings_merged)
@@ -1,326 +1,315 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Pylons middleware initialization
23 23 """
24 24 import logging
25 25
26 26 from paste.registry import RegistryManager
27 27 from paste.gzipper import make_gzip_middleware
28 28 from pylons.middleware import ErrorHandler, StatusCodeRedirect
29 29 from pylons.wsgiapp import PylonsApp
30 30 from pyramid.authorization import ACLAuthorizationPolicy
31 31 from pyramid.config import Configurator
32 32 from pyramid.static import static_view
33 33 from pyramid.settings import asbool, aslist
34 34 from pyramid.wsgi import wsgiapp
35 35 from routes.middleware import RoutesMiddleware
36 36 import routes.util
37 37
38 38 import rhodecode
39 from rhodecode.config import patches, utils
40 from rhodecode.config.environment import load_environment
39 from rhodecode.config import patches
40 from rhodecode.config.environment import (
41 load_environment, load_pyramid_environment)
41 42 from rhodecode.lib.middleware import csrf
42 43 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
43 44 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
44 45 from rhodecode.lib.middleware.https_fixup import HttpsFixup
45 46 from rhodecode.lib.middleware.vcs import VCSMiddleware
46 47 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
47 48
48 49
49 50 log = logging.getLogger(__name__)
50 51
51 52
52 53 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
53 54 """Create a Pylons WSGI application and return it
54 55
55 56 ``global_conf``
56 57 The inherited configuration for this application. Normally from
57 58 the [DEFAULT] section of the Paste ini file.
58 59
59 60 ``full_stack``
60 61 Whether or not this application provides a full WSGI stack (by
61 62 default, meaning it handles its own exceptions and errors).
62 63 Disable full_stack when this application is "managed" by
63 64 another WSGI middleware.
64 65
65 66 ``app_conf``
66 67 The application's local configuration. Normally specified in
67 68 the [app:<name>] section of the Paste ini file (where <name>
68 69 defaults to main).
69 70
70 71 """
71 72 # Apply compatibility patches
72 73 patches.kombu_1_5_1_python_2_7_11()
73 74 patches.inspect_getargspec()
74 75
75 76 # Configure the Pylons environment
76 77 config = load_environment(global_conf, app_conf)
77 78
78 79 # The Pylons WSGI app
79 80 app = PylonsApp(config=config)
80 81 if rhodecode.is_test:
81 82 app = csrf.CSRFDetector(app)
82 83
83 84 expected_origin = config.get('expected_origin')
84 85 if expected_origin:
85 86 # The API can be accessed from other Origins.
86 87 app = csrf.OriginChecker(app, expected_origin,
87 88 skip_urls=[routes.util.url_for('api')])
88 89
89 90 # Add RoutesMiddleware. Currently we have two instances in the stack. This
90 91 # is the lower one to make the StatusCodeRedirect middleware happy.
91 92 # TODO: johbo: This is not optimal, search for a better solution.
92 93 app = RoutesMiddleware(app, config['routes.map'])
93 94
94 95 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
95 96 if asbool(config['pdebug']):
96 97 from rhodecode.lib.profiler import ProfilingMiddleware
97 98 app = ProfilingMiddleware(app)
98 99
99 100 # Protect from VCS Server error related pages when server is not available
100 101 vcs_server_enabled = asbool(config.get('vcs.server.enable', 'true'))
101 102 if not vcs_server_enabled:
102 103 app = DisableVCSPagesWrapper(app)
103 104
104 105 if asbool(full_stack):
105 106
106 107 # Appenlight monitoring and error handler
107 108 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
108 109
109 110 # Handle Python exceptions
110 111 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
111 112
112 113 # we want our low level middleware to get to the request ASAP. We don't
113 114 # need any pylons stack middleware in them
114 115 app = VCSMiddleware(app, config, appenlight_client)
115 116 # Display error documents for 401, 403, 404 status codes (and
116 117 # 500 when debug is disabled)
117 118 if asbool(config['debug']):
118 119 app = StatusCodeRedirect(app)
119 120 else:
120 121 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
121 122
122 123 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
123 124 app = HttpsFixup(app, config)
124 125
125 126 # Establish the Registry for this application
126 127 app = RegistryManager(app)
127 128
128 129 app.config = config
129 130
130 131 return app
131 132
132 133
133 134 def make_pyramid_app(global_config, **settings):
134 135 """
135 136 Constructs the WSGI application based on Pyramid and wraps the Pylons based
136 137 application.
137 138
138 139 Specials:
139 140
140 141 * We migrate from Pylons to Pyramid. While doing this, we keep both
141 142 frameworks functional. This involves moving some WSGI middlewares around
142 143 and providing access to some data internals, so that the old code is
143 144 still functional.
144 145
145 146 * The application can also be integrated like a plugin via the call to
146 147 `includeme`. This is accompanied with the other utility functions which
147 148 are called. Changing this should be done with great care to not break
148 149 cases when these fragments are assembled from another place.
149 150
150 151 """
151 152 # The edition string should be available in pylons too, so we add it here
152 153 # before copying the settings.
153 154 settings.setdefault('rhodecode.edition', 'Community Edition')
154 155
155 156 # As long as our Pylons application does expect "unprepared" settings, make
156 157 # sure that we keep an unmodified copy. This avoids unintentional change of
157 158 # behavior in the old application.
158 159 settings_pylons = settings.copy()
159 160
160 # Some parts of the code expect a merge of global and app settings.
161 settings_merged = global_config.copy()
162 settings_merged.update(settings)
163
164 161 sanitize_settings_and_apply_defaults(settings)
165 162 config = Configurator(settings=settings)
166 163 add_pylons_compat_data(config.registry, global_config, settings_pylons)
167 164
168 # If this is a test run we prepare the test environment like
169 # creating a test database, test search index and test repositories.
170 # This has to be done before the database connection is initialized.
171 if settings['is_test']:
172 rhodecode.is_test = True
173 utils.initialize_test_environment(settings_merged)
174
175 # Initialize the database connection.
176 utils.initialize_database(settings_merged)
165 load_pyramid_environment(global_config, settings)
177 166
178 167 includeme(config)
179 168 includeme_last(config)
180 169 pyramid_app = config.make_wsgi_app()
181 170 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
182 171 return pyramid_app
183 172
184 173
185 174 def add_pylons_compat_data(registry, global_config, settings):
186 175 """
187 176 Attach data to the registry to support the Pylons integration.
188 177 """
189 178 registry._pylons_compat_global_config = global_config
190 179 registry._pylons_compat_settings = settings
191 180
192 181
193 182 def includeme(config):
194 183 settings = config.registry.settings
195 184
196 185 # Includes which are required. The application would fail without them.
197 186 config.include('pyramid_mako')
198 187 config.include('pyramid_beaker')
199 188 config.include('rhodecode.authentication')
200 189 config.include('rhodecode.login')
201 190 config.include('rhodecode.tweens')
202 191 config.include('rhodecode.api')
203 192
204 193 # Set the authorization policy.
205 194 authz_policy = ACLAuthorizationPolicy()
206 195 config.set_authorization_policy(authz_policy)
207 196
208 197 # Set the default renderer for HTML templates to mako.
209 198 config.add_mako_renderer('.html')
210 199
211 200 # plugin information
212 201 config.registry.rhodecode_plugins = {}
213 202
214 203 config.add_directive(
215 204 'register_rhodecode_plugin', register_rhodecode_plugin)
216 205 # include RhodeCode plugins
217 206 includes = aslist(settings.get('rhodecode.includes', []))
218 207 for inc in includes:
219 208 config.include(inc)
220 209
221 210 # This is the glue which allows us to migrate in chunks. By registering the
222 211 # pylons based application as the "Not Found" view in Pyramid, we will
223 212 # fallback to the old application each time the new one does not yet know
224 213 # how to handle a request.
225 214 pylons_app = make_app(
226 215 config.registry._pylons_compat_global_config,
227 216 **config.registry._pylons_compat_settings)
228 217 config.registry._pylons_compat_config = pylons_app.config
229 218 pylons_app_as_view = wsgiapp(pylons_app)
230 219 config.add_notfound_view(pylons_app_as_view)
231 220
232 221
233 222 def includeme_last(config):
234 223 """
235 224 The static file catchall needs to be last in the view configuration.
236 225 """
237 226 settings = config.registry.settings
238 227
239 228 # Note: johbo: I would prefer to register a prefix for static files at some
240 229 # point, e.g. move them under '_static/'. This would fully avoid that we
241 230 # can have name clashes with a repository name. Imaging someone calling his
242 231 # repo "css" ;-) Also having an external web server to serve out the static
243 232 # files seems to be easier to set up if they have a common prefix.
244 233 #
245 234 # Example: config.add_static_view('_static', path='rhodecode:public')
246 235 #
247 236 # It might be an option to register both paths for a while and then migrate
248 237 # over to the new location.
249 238
250 239 # Serving static files with a catchall.
251 240 if settings['static_files']:
252 241 config.add_route('catchall_static', '/*subpath')
253 242 config.add_view(
254 243 static_view('rhodecode:public'), route_name='catchall_static')
255 244
256 245
257 246 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
258 247 """
259 248 Apply outer WSGI middlewares around the application.
260 249
261 250 Part of this has been moved up from the Pylons layer, so that the
262 251 data is also available if old Pylons code is hit through an already ported
263 252 view.
264 253 """
265 254 settings = config.registry.settings
266 255
267 256 # Add RoutesMiddleware. Currently we have two instances in the stack. This
268 257 # is the upper one to support the pylons compatibility tween during
269 258 # migration to pyramid.
270 259 pyramid_app = RoutesMiddleware(
271 260 pyramid_app, config.registry._pylons_compat_config['routes.map'])
272 261
273 262 # TODO: johbo: Don't really see why we enable the gzip middleware when
274 263 # serving static files, might be something that should have its own setting
275 264 # as well?
276 265 if settings['static_files']:
277 266 pyramid_app = make_gzip_middleware(
278 267 pyramid_app, settings, compress_level=1)
279 268
280 269 return pyramid_app
281 270
282 271
283 272 def sanitize_settings_and_apply_defaults(settings):
284 273 """
285 274 Applies settings defaults and does all type conversion.
286 275
287 276 We would move all settings parsing and preparation into this place, so that
288 277 we have only one place left which deals with this part. The remaining parts
289 278 of the application would start to rely fully on well prepared settings.
290 279
291 280 This piece would later be split up per topic to avoid a big fat monster
292 281 function.
293 282 """
294 283
295 284 # Pyramid's mako renderer has to search in the templates folder so that the
296 285 # old templates still work. Ported and new templates are expected to use
297 286 # real asset specifications for the includes.
298 287 mako_directories = settings.setdefault('mako.directories', [
299 288 # Base templates of the original Pylons application
300 289 'rhodecode:templates',
301 290 ])
302 291 log.debug(
303 292 "Using the following Mako template directories: %s",
304 293 mako_directories)
305 294
306 295 # Default includes, possible to change as a user
307 296 pyramid_includes = settings.setdefault('pyramid.includes', [
308 297 'rhodecode.lib.middleware.request_wrapper',
309 298 ])
310 299 log.debug(
311 300 "Using the following pyramid.includes: %s",
312 301 pyramid_includes)
313 302
314 303 # TODO: johbo: Re-think this, usually the call to config.include
315 304 # should allow to pass in a prefix.
316 305 settings.setdefault('rhodecode.api.url', '/_admin/api')
317 306
318 307 _bool_setting(settings, 'vcs.server.enable', 'true')
319 308 _bool_setting(settings, 'static_files', 'true')
320 309 _bool_setting(settings, 'is_test', 'false')
321 310
322 311 return settings
323 312
324 313
325 314 def _bool_setting(settings, name, default):
326 315 settings[name] = asbool(settings.get(name, default))
General Comments 0
You need to be logged in to leave comments. Login now