##// END OF EJS Templates
db: Set `rhodecode.is_test` in `make_pyramid_app` instead of `make_app`...
johbo -
r118:931f58e5 default
parent child Browse files
Show More
@@ -1,177 +1,176 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 rhodecode.is_test = str2bool(app_conf.get('is_test', 'False'))
66 65
67 66 # Pylons paths
68 67 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
69 68 paths = {
70 69 'root': root,
71 70 'controllers': os.path.join(root, 'controllers'),
72 71 'static_files': os.path.join(root, 'public'),
73 72 'templates': [os.path.join(root, 'templates')],
74 73 }
75 74
76 75 # Initialize config with the basic options
77 76 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
78 77
79 78 # store some globals into rhodecode
80 79 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
81 80 rhodecode.CELERY_EAGER = str2bool(
82 81 config['app_conf'].get('celery.always.eager'))
83 82
84 83 config['routes.map'] = make_map(config)
85 84
86 85 if asbool(config['debug']):
87 86 jsroutes = config['routes.map'].jsroutes()
88 87 jsroutes_file_content = generate_jsroutes_content(jsroutes)
89 88 jsroutes_file_path = os.path.join(
90 89 paths['static_files'], 'js', 'rhodecode', 'routes.js')
91 90
92 91 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
93 92 f.write(jsroutes_file_content)
94 93
95 94 config['pylons.app_globals'] = app_globals.Globals(config)
96 95 config['pylons.h'] = helpers
97 96 rhodecode.CONFIG = config
98 97
99 98 load_rcextensions(root_path=config['here'])
100 99
101 100 # Setup cache object as early as possible
102 101 import pylons
103 102 pylons.cache._push_object(config['pylons.app_globals'].cache)
104 103
105 104 # Create the Mako TemplateLookup, with the default auto-escaping
106 105 config['pylons.app_globals'].mako_lookup = TemplateLookup(
107 106 directories=paths['templates'],
108 107 error_handler=handle_mako_error,
109 108 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
110 109 input_encoding='utf-8', default_filters=['escape'],
111 110 imports=['from webhelpers.html import escape'])
112 111
113 112 # sets the c attribute access when don't existing attribute are accessed
114 113 config['pylons.strict_tmpl_context'] = True
115 114
116 115 # Limit backends to "vcs.backends" from configuration
117 116 backends = config['vcs.backends'] = aslist(
118 117 config.get('vcs.backends', 'hg,git'), sep=',')
119 118 for alias in rhodecode.BACKENDS.keys():
120 119 if alias not in backends:
121 120 del rhodecode.BACKENDS[alias]
122 121 log.info("Enabled backends: %s", backends)
123 122
124 123 # initialize vcs client and optionally run the server if enabled
125 124 vcs_server_uri = config.get('vcs.server', '')
126 125 vcs_server_enabled = str2bool(config.get('vcs.server.enable', 'true'))
127 126 start_server = (
128 127 str2bool(config.get('vcs.start_server', 'false')) and
129 128 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
130 129 if vcs_server_enabled and start_server:
131 130 log.info("Starting vcsserver")
132 131 start_vcs_server(server_and_port=vcs_server_uri,
133 132 protocol=utils.get_vcs_server_protocol(config),
134 133 log_level=config['vcs.server.log_level'])
135 134
136 135 set_available_permissions(config)
137 136 db_cfg = make_db_config(clear_session=True)
138 137
139 138 repos_path = list(db_cfg.items('paths'))[0][1]
140 139 config['base_path'] = repos_path
141 140
142 141 config['vcs.hooks.direct_calls'] = _use_direct_hook_calls(config)
143 142 config['vcs.hooks.protocol'] = _get_vcs_hooks_protocol(config)
144 143
145 144 # store db config also in main global CONFIG
146 145 set_rhodecode_config(config)
147 146
148 147 # configure instance id
149 148 utils.set_instance_id(config)
150 149
151 150 # CONFIGURATION OPTIONS HERE (note: all config options will override
152 151 # any Pylons config options)
153 152
154 153 # store config reference into our module to skip import magic of pylons
155 154 rhodecode.CONFIG.update(config)
156 155
157 156 utils.configure_pyro4(config)
158 157 utils.configure_vcs(config)
159 158 if vcs_server_enabled:
160 159 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(config))
161 160
162 161 import_on_startup = str2bool(config.get('startup.import_repos', False))
163 162 if vcs_server_enabled and import_on_startup:
164 163 repo2db_mapper(ScmModel().repo_scan(repos_path), remove_obsolete=False)
165 164 return config
166 165
167 166
168 167 def _use_direct_hook_calls(config):
169 168 default_direct_hook_calls = 'false'
170 169 direct_hook_calls = str2bool(
171 170 config.get('vcs.hooks.direct_calls', default_direct_hook_calls))
172 171 return direct_hook_calls
173 172
174 173
175 174 def _get_vcs_hooks_protocol(config):
176 175 protocol = config.get('vcs.hooks.protocol', 'pyro4').lower()
177 176 return protocol
@@ -1,324 +1,327 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 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 168 # If this is a test run we prepare the test environment like
169 169 # creating a test database, test search index and test repositories.
170 170 # This has to be done before the database connection is initialized.
171 # if settings['is_test']:
171 172 if settings['is_test']:
173 rhodecode.is_test = True
172 174 utils.initialize_test_environment(settings_merged)
173 175
174 176 # Initialize the database connection.
175 177 utils.initialize_database(settings_merged)
176 178
177 179 includeme(config)
178 180 includeme_last(config)
179 181 pyramid_app = config.make_wsgi_app()
180 182 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
181 183 return pyramid_app
182 184
183 185
184 186 def add_pylons_compat_data(registry, global_config, settings):
185 187 """
186 188 Attach data to the registry to support the Pylons integration.
187 189 """
188 190 registry._pylons_compat_global_config = global_config
189 191 registry._pylons_compat_settings = settings
190 192
191 193
192 194 def includeme(config):
193 195 settings = config.registry.settings
194 196
195 197 # Includes which are required. The application would fail without them.
196 198 config.include('pyramid_mako')
197 199 config.include('pyramid_beaker')
198 200 config.include('rhodecode.authentication')
199 201 config.include('rhodecode.login')
200 202 config.include('rhodecode.tweens')
201 203 config.include('rhodecode.api')
202 204
203 205 # Set the authorization policy.
204 206 authz_policy = ACLAuthorizationPolicy()
205 207 config.set_authorization_policy(authz_policy)
206 208
207 209 # Set the default renderer for HTML templates to mako.
208 210 config.add_mako_renderer('.html')
209 211
210 212 # plugin information
211 213 config.registry.rhodecode_plugins = {}
212 214
213 215 config.add_directive(
214 216 'register_rhodecode_plugin', register_rhodecode_plugin)
215 217 # include RhodeCode plugins
216 218 includes = aslist(settings.get('rhodecode.includes', []))
217 219 for inc in includes:
218 220 config.include(inc)
219 221
220 222 # This is the glue which allows us to migrate in chunks. By registering the
221 223 # pylons based application as the "Not Found" view in Pyramid, we will
222 224 # fallback to the old application each time the new one does not yet know
223 225 # how to handle a request.
224 226 pylons_app = make_app(
225 227 config.registry._pylons_compat_global_config,
226 228 **config.registry._pylons_compat_settings)
227 229 config.registry._pylons_compat_config = pylons_app.config
228 230 pylons_app_as_view = wsgiapp(pylons_app)
229 231 config.add_notfound_view(pylons_app_as_view)
230 232
231 233
232 234 def includeme_last(config):
233 235 """
234 236 The static file catchall needs to be last in the view configuration.
235 237 """
236 238 settings = config.registry.settings
237 239
238 240 # Note: johbo: I would prefer to register a prefix for static files at some
239 241 # point, e.g. move them under '_static/'. This would fully avoid that we
240 242 # can have name clashes with a repository name. Imaging someone calling his
241 243 # repo "css" ;-) Also having an external web server to serve out the static
242 244 # files seems to be easier to set up if they have a common prefix.
243 245 #
244 246 # Example: config.add_static_view('_static', path='rhodecode:public')
245 247 #
246 248 # It might be an option to register both paths for a while and then migrate
247 249 # over to the new location.
248 250
249 251 # Serving static files with a catchall.
250 252 if settings['static_files']:
251 253 config.add_route('catchall_static', '/*subpath')
252 254 config.add_view(
253 255 static_view('rhodecode:public'), route_name='catchall_static')
254 256
255 257
256 258 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
257 259 """
258 260 Apply outer WSGI middlewares around the application.
259 261
260 262 Part of this has been moved up from the Pylons layer, so that the
261 263 data is also available if old Pylons code is hit through an already ported
262 264 view.
263 265 """
264 266 settings = config.registry.settings
265 267
266 268 # Add RoutesMiddleware. Currently we have two instances in the stack. This
267 269 # is the upper one to support the pylons compatibility tween during
268 270 # migration to pyramid.
269 271 pyramid_app = RoutesMiddleware(
270 272 pyramid_app, config.registry._pylons_compat_config['routes.map'])
271 273
272 274 # TODO: johbo: Don't really see why we enable the gzip middleware when
273 275 # serving static files, might be something that should have its own setting
274 276 # as well?
275 277 if settings['static_files']:
276 278 pyramid_app = make_gzip_middleware(
277 279 pyramid_app, settings, compress_level=1)
278 280
279 281 return pyramid_app
280 282
281 283
282 284 def sanitize_settings_and_apply_defaults(settings):
283 285 """
284 286 Applies settings defaults and does all type conversion.
285 287
286 288 We would move all settings parsing and preparation into this place, so that
287 289 we have only one place left which deals with this part. The remaining parts
288 290 of the application would start to rely fully on well prepared settings.
289 291
290 292 This piece would later be split up per topic to avoid a big fat monster
291 293 function.
292 294 """
293 295
294 296 # Pyramid's mako renderer has to search in the templates folder so that the
295 297 # old templates still work. Ported and new templates are expected to use
296 298 # real asset specifications for the includes.
297 299 mako_directories = settings.setdefault('mako.directories', [
298 300 # Base templates of the original Pylons application
299 301 'rhodecode:templates',
300 302 ])
301 303 log.debug(
302 304 "Using the following Mako template directories: %s",
303 305 mako_directories)
304 306
305 307 # Default includes, possible to change as a user
306 308 pyramid_includes = settings.setdefault('pyramid.includes', [
307 309 'rhodecode.lib.middleware.request_wrapper',
308 310 ])
309 311 log.debug(
310 312 "Using the following pyramid.includes: %s",
311 313 pyramid_includes)
312 314
313 315 # TODO: johbo: Re-think this, usually the call to config.include
314 316 # should allow to pass in a prefix.
315 317 settings.setdefault('rhodecode.api.url', '/_admin/api')
316 318
317 319 _bool_setting(settings, 'vcs.server.enable', 'true')
318 320 _bool_setting(settings, 'static_files', 'true')
321 _bool_setting(settings, 'is_test', 'false')
319 322
320 323 return settings
321 324
322 325
323 326 def _bool_setting(settings, name, default):
324 327 settings[name] = asbool(settings.get(name, default))
General Comments 0
You need to be logged in to leave comments. Login now