##// END OF EJS Templates
db: Change ordering of test env setup and db connection init....
johbo -
r117:25511505 default
parent child Browse files
Show More
@@ -1,323 +1,324
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 39 from rhodecode.config import patches, utils
40 40 from rhodecode.config.environment import load_environment
41 41 from rhodecode.lib.middleware import csrf
42 42 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
43 43 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
44 44 from rhodecode.lib.middleware.https_fixup import HttpsFixup
45 45 from rhodecode.lib.middleware.vcs import VCSMiddleware
46 46 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
47 47
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
53 53 """Create a Pylons WSGI application and return it
54 54
55 55 ``global_conf``
56 56 The inherited configuration for this application. Normally from
57 57 the [DEFAULT] section of the Paste ini file.
58 58
59 59 ``full_stack``
60 60 Whether or not this application provides a full WSGI stack (by
61 61 default, meaning it handles its own exceptions and errors).
62 62 Disable full_stack when this application is "managed" by
63 63 another WSGI middleware.
64 64
65 65 ``app_conf``
66 66 The application's local configuration. Normally specified in
67 67 the [app:<name>] section of the Paste ini file (where <name>
68 68 defaults to main).
69 69
70 70 """
71 71 # Apply compatibility patches
72 72 patches.kombu_1_5_1_python_2_7_11()
73 73 patches.inspect_getargspec()
74 74
75 75 # Configure the Pylons environment
76 76 config = load_environment(global_conf, app_conf)
77 77
78 78 # The Pylons WSGI app
79 79 app = PylonsApp(config=config)
80 80 if rhodecode.is_test:
81 81 app = csrf.CSRFDetector(app)
82 82
83 83 expected_origin = config.get('expected_origin')
84 84 if expected_origin:
85 85 # The API can be accessed from other Origins.
86 86 app = csrf.OriginChecker(app, expected_origin,
87 87 skip_urls=[routes.util.url_for('api')])
88 88
89 89 # Add RoutesMiddleware. Currently we have two instances in the stack. This
90 90 # is the lower one to make the StatusCodeRedirect middleware happy.
91 91 # TODO: johbo: This is not optimal, search for a better solution.
92 92 app = RoutesMiddleware(app, config['routes.map'])
93 93
94 94 # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
95 95 if asbool(config['pdebug']):
96 96 from rhodecode.lib.profiler import ProfilingMiddleware
97 97 app = ProfilingMiddleware(app)
98 98
99 99 # Protect from VCS Server error related pages when server is not available
100 100 vcs_server_enabled = asbool(config.get('vcs.server.enable', 'true'))
101 101 if not vcs_server_enabled:
102 102 app = DisableVCSPagesWrapper(app)
103 103
104 104 if asbool(full_stack):
105 105
106 106 # Appenlight monitoring and error handler
107 107 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
108 108
109 109 # Handle Python exceptions
110 110 app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
111 111
112 112 # we want our low level middleware to get to the request ASAP. We don't
113 113 # need any pylons stack middleware in them
114 114 app = VCSMiddleware(app, config, appenlight_client)
115 115 # Display error documents for 401, 403, 404 status codes (and
116 116 # 500 when debug is disabled)
117 117 if asbool(config['debug']):
118 118 app = StatusCodeRedirect(app)
119 119 else:
120 120 app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
121 121
122 122 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
123 123 app = HttpsFixup(app, config)
124 124
125 125 # Establish the Registry for this application
126 126 app = RegistryManager(app)
127 127
128 128 app.config = config
129 129
130 130 return app
131 131
132 132
133 133 def make_pyramid_app(global_config, **settings):
134 134 """
135 135 Constructs the WSGI application based on Pyramid and wraps the Pylons based
136 136 application.
137 137
138 138 Specials:
139 139
140 140 * We migrate from Pylons to Pyramid. While doing this, we keep both
141 141 frameworks functional. This involves moving some WSGI middlewares around
142 142 and providing access to some data internals, so that the old code is
143 143 still functional.
144 144
145 145 * The application can also be integrated like a plugin via the call to
146 146 `includeme`. This is accompanied with the other utility functions which
147 147 are called. Changing this should be done with great care to not break
148 148 cases when these fragments are assembled from another place.
149 149
150 150 """
151 151 # The edition string should be available in pylons too, so we add it here
152 152 # before copying the settings.
153 153 settings.setdefault('rhodecode.edition', 'Community Edition')
154 154
155 155 # As long as our Pylons application does expect "unprepared" settings, make
156 156 # sure that we keep an unmodified copy. This avoids unintentional change of
157 157 # behavior in the old application.
158 158 settings_pylons = settings.copy()
159 159
160 160 # Some parts of the code expect a merge of global and app settings.
161 161 settings_merged = global_config.copy()
162 162 settings_merged.update(settings)
163 163
164 164 sanitize_settings_and_apply_defaults(settings)
165 165 config = Configurator(settings=settings)
166 166 add_pylons_compat_data(config.registry, global_config, settings_pylons)
167 167
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 utils.initialize_test_environment(settings_merged)
173
168 174 # Initialize the database connection.
169 175 utils.initialize_database(settings_merged)
170 176
171 # If this is a test run we prepare the test environment like
172 # creating a test database, test search index and test repositories.
173 if settings['is_test']:
174 utils.initialize_test_environment(settings_merged)
175
176 177 includeme(config)
177 178 includeme_last(config)
178 179 pyramid_app = config.make_wsgi_app()
179 180 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
180 181 return pyramid_app
181 182
182 183
183 184 def add_pylons_compat_data(registry, global_config, settings):
184 185 """
185 186 Attach data to the registry to support the Pylons integration.
186 187 """
187 188 registry._pylons_compat_global_config = global_config
188 189 registry._pylons_compat_settings = settings
189 190
190 191
191 192 def includeme(config):
192 193 settings = config.registry.settings
193 194
194 195 # Includes which are required. The application would fail without them.
195 196 config.include('pyramid_mako')
196 197 config.include('pyramid_beaker')
197 198 config.include('rhodecode.authentication')
198 199 config.include('rhodecode.login')
199 200 config.include('rhodecode.tweens')
200 201 config.include('rhodecode.api')
201 202
202 203 # Set the authorization policy.
203 204 authz_policy = ACLAuthorizationPolicy()
204 205 config.set_authorization_policy(authz_policy)
205 206
206 207 # Set the default renderer for HTML templates to mako.
207 208 config.add_mako_renderer('.html')
208 209
209 210 # plugin information
210 211 config.registry.rhodecode_plugins = {}
211 212
212 213 config.add_directive(
213 214 'register_rhodecode_plugin', register_rhodecode_plugin)
214 215 # include RhodeCode plugins
215 216 includes = aslist(settings.get('rhodecode.includes', []))
216 217 for inc in includes:
217 218 config.include(inc)
218 219
219 220 # This is the glue which allows us to migrate in chunks. By registering the
220 221 # pylons based application as the "Not Found" view in Pyramid, we will
221 222 # fallback to the old application each time the new one does not yet know
222 223 # how to handle a request.
223 224 pylons_app = make_app(
224 225 config.registry._pylons_compat_global_config,
225 226 **config.registry._pylons_compat_settings)
226 227 config.registry._pylons_compat_config = pylons_app.config
227 228 pylons_app_as_view = wsgiapp(pylons_app)
228 229 config.add_notfound_view(pylons_app_as_view)
229 230
230 231
231 232 def includeme_last(config):
232 233 """
233 234 The static file catchall needs to be last in the view configuration.
234 235 """
235 236 settings = config.registry.settings
236 237
237 238 # Note: johbo: I would prefer to register a prefix for static files at some
238 239 # point, e.g. move them under '_static/'. This would fully avoid that we
239 240 # can have name clashes with a repository name. Imaging someone calling his
240 241 # repo "css" ;-) Also having an external web server to serve out the static
241 242 # files seems to be easier to set up if they have a common prefix.
242 243 #
243 244 # Example: config.add_static_view('_static', path='rhodecode:public')
244 245 #
245 246 # It might be an option to register both paths for a while and then migrate
246 247 # over to the new location.
247 248
248 249 # Serving static files with a catchall.
249 250 if settings['static_files']:
250 251 config.add_route('catchall_static', '/*subpath')
251 252 config.add_view(
252 253 static_view('rhodecode:public'), route_name='catchall_static')
253 254
254 255
255 256 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
256 257 """
257 258 Apply outer WSGI middlewares around the application.
258 259
259 260 Part of this has been moved up from the Pylons layer, so that the
260 261 data is also available if old Pylons code is hit through an already ported
261 262 view.
262 263 """
263 264 settings = config.registry.settings
264 265
265 266 # Add RoutesMiddleware. Currently we have two instances in the stack. This
266 267 # is the upper one to support the pylons compatibility tween during
267 268 # migration to pyramid.
268 269 pyramid_app = RoutesMiddleware(
269 270 pyramid_app, config.registry._pylons_compat_config['routes.map'])
270 271
271 272 # TODO: johbo: Don't really see why we enable the gzip middleware when
272 273 # serving static files, might be something that should have its own setting
273 274 # as well?
274 275 if settings['static_files']:
275 276 pyramid_app = make_gzip_middleware(
276 277 pyramid_app, settings, compress_level=1)
277 278
278 279 return pyramid_app
279 280
280 281
281 282 def sanitize_settings_and_apply_defaults(settings):
282 283 """
283 284 Applies settings defaults and does all type conversion.
284 285
285 286 We would move all settings parsing and preparation into this place, so that
286 287 we have only one place left which deals with this part. The remaining parts
287 288 of the application would start to rely fully on well prepared settings.
288 289
289 290 This piece would later be split up per topic to avoid a big fat monster
290 291 function.
291 292 """
292 293
293 294 # Pyramid's mako renderer has to search in the templates folder so that the
294 295 # old templates still work. Ported and new templates are expected to use
295 296 # real asset specifications for the includes.
296 297 mako_directories = settings.setdefault('mako.directories', [
297 298 # Base templates of the original Pylons application
298 299 'rhodecode:templates',
299 300 ])
300 301 log.debug(
301 302 "Using the following Mako template directories: %s",
302 303 mako_directories)
303 304
304 305 # Default includes, possible to change as a user
305 306 pyramid_includes = settings.setdefault('pyramid.includes', [
306 307 'rhodecode.lib.middleware.request_wrapper',
307 308 ])
308 309 log.debug(
309 310 "Using the following pyramid.includes: %s",
310 311 pyramid_includes)
311 312
312 313 # TODO: johbo: Re-think this, usually the call to config.include
313 314 # should allow to pass in a prefix.
314 315 settings.setdefault('rhodecode.api.url', '/_admin/api')
315 316
316 317 _bool_setting(settings, 'vcs.server.enable', 'true')
317 318 _bool_setting(settings, 'static_files', 'true')
318 319
319 320 return settings
320 321
321 322
322 323 def _bool_setting(settings, name, default):
323 324 settings[name] = asbool(settings.get(name, default))
General Comments 0
You need to be logged in to leave comments. Login now