##// END OF EJS Templates
config: Move initial repo scan up to the pyramid layer....
Martin Bornhold -
r580:b2c504de default
parent child Browse files
Show More
@@ -1,199 +1,196 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons environment configuration
22 Pylons environment configuration
23 """
23 """
24
24
25 import os
25 import os
26 import logging
26 import logging
27 import rhodecode
27 import rhodecode
28 import platform
28 import platform
29 import re
29 import re
30 import io
30 import io
31
31
32 from mako.lookup import TemplateLookup
32 from mako.lookup import TemplateLookup
33 from pylons.configuration import PylonsConfig
33 from pylons.configuration import PylonsConfig
34 from pylons.error import handle_mako_error
34 from pylons.error import handle_mako_error
35 from pyramid.settings import asbool
35 from pyramid.settings import asbool
36
36
37 # don't remove this import it does magic for celery
37 # don't remove this import it does magic for celery
38 from rhodecode.lib import celerypylons # noqa
38 from rhodecode.lib import celerypylons # noqa
39
39
40 import rhodecode.lib.app_globals as app_globals
40 import rhodecode.lib.app_globals as app_globals
41
41
42 from rhodecode.config import utils
42 from rhodecode.config import utils
43 from rhodecode.config.routing import make_map
43 from rhodecode.config.routing import make_map
44 from rhodecode.config.jsroutes import generate_jsroutes_content
44 from rhodecode.config.jsroutes import generate_jsroutes_content
45
45
46 from rhodecode.lib import helpers
46 from rhodecode.lib import helpers
47 from rhodecode.lib.auth import set_available_permissions
47 from rhodecode.lib.auth import set_available_permissions
48 from rhodecode.lib.utils import (
48 from rhodecode.lib.utils import (
49 repo2db_mapper, make_db_config, set_rhodecode_config,
49 repo2db_mapper, make_db_config, set_rhodecode_config,
50 load_rcextensions)
50 load_rcextensions)
51 from rhodecode.lib.utils2 import str2bool, aslist
51 from rhodecode.lib.utils2 import str2bool, aslist
52 from rhodecode.lib.vcs import connect_vcs, start_vcs_server
52 from rhodecode.lib.vcs import connect_vcs, start_vcs_server
53 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.scm import ScmModel
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 def load_environment(global_conf, app_conf, initial=False,
57 def load_environment(global_conf, app_conf, initial=False,
58 test_env=None, test_index=None):
58 test_env=None, test_index=None):
59 """
59 """
60 Configure the Pylons environment via the ``pylons.config``
60 Configure the Pylons environment via the ``pylons.config``
61 object
61 object
62 """
62 """
63 config = PylonsConfig()
63 config = PylonsConfig()
64
64
65
65
66 # Pylons paths
66 # Pylons paths
67 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
67 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
68 paths = {
68 paths = {
69 'root': root,
69 'root': root,
70 'controllers': os.path.join(root, 'controllers'),
70 'controllers': os.path.join(root, 'controllers'),
71 'static_files': os.path.join(root, 'public'),
71 'static_files': os.path.join(root, 'public'),
72 'templates': [os.path.join(root, 'templates')],
72 'templates': [os.path.join(root, 'templates')],
73 }
73 }
74
74
75 # Initialize config with the basic options
75 # Initialize config with the basic options
76 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
76 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
77
77
78 # store some globals into rhodecode
78 # store some globals into rhodecode
79 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
79 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
80 rhodecode.CELERY_EAGER = str2bool(
80 rhodecode.CELERY_EAGER = str2bool(
81 config['app_conf'].get('celery.always.eager'))
81 config['app_conf'].get('celery.always.eager'))
82
82
83 config['routes.map'] = make_map(config)
83 config['routes.map'] = make_map(config)
84
84
85 if asbool(config.get('generate_js_files', 'false')):
85 if asbool(config.get('generate_js_files', 'false')):
86 jsroutes = config['routes.map'].jsroutes()
86 jsroutes = config['routes.map'].jsroutes()
87 jsroutes_file_content = generate_jsroutes_content(jsroutes)
87 jsroutes_file_content = generate_jsroutes_content(jsroutes)
88 jsroutes_file_path = os.path.join(
88 jsroutes_file_path = os.path.join(
89 paths['static_files'], 'js', 'rhodecode', 'routes.js')
89 paths['static_files'], 'js', 'rhodecode', 'routes.js')
90
90
91 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
91 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
92 f.write(jsroutes_file_content)
92 f.write(jsroutes_file_content)
93
93
94 config['pylons.app_globals'] = app_globals.Globals(config)
94 config['pylons.app_globals'] = app_globals.Globals(config)
95 config['pylons.h'] = helpers
95 config['pylons.h'] = helpers
96 rhodecode.CONFIG = config
96 rhodecode.CONFIG = config
97
97
98 load_rcextensions(root_path=config['here'])
98 load_rcextensions(root_path=config['here'])
99
99
100 # Setup cache object as early as possible
100 # Setup cache object as early as possible
101 import pylons
101 import pylons
102 pylons.cache._push_object(config['pylons.app_globals'].cache)
102 pylons.cache._push_object(config['pylons.app_globals'].cache)
103
103
104 # Create the Mako TemplateLookup, with the default auto-escaping
104 # Create the Mako TemplateLookup, with the default auto-escaping
105 config['pylons.app_globals'].mako_lookup = TemplateLookup(
105 config['pylons.app_globals'].mako_lookup = TemplateLookup(
106 directories=paths['templates'],
106 directories=paths['templates'],
107 error_handler=handle_mako_error,
107 error_handler=handle_mako_error,
108 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
108 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
109 input_encoding='utf-8', default_filters=['escape'],
109 input_encoding='utf-8', default_filters=['escape'],
110 imports=['from webhelpers.html import escape'])
110 imports=['from webhelpers.html import escape'])
111
111
112 # sets the c attribute access when don't existing attribute are accessed
112 # sets the c attribute access when don't existing attribute are accessed
113 config['pylons.strict_tmpl_context'] = True
113 config['pylons.strict_tmpl_context'] = True
114
114
115 # configure channelstream
115 # configure channelstream
116 config['channelstream_config'] = {
116 config['channelstream_config'] = {
117 'enabled': asbool(config.get('channelstream.enabled', False)),
117 'enabled': asbool(config.get('channelstream.enabled', False)),
118 'server': config.get('channelstream.server'),
118 'server': config.get('channelstream.server'),
119 'secret': config.get('channelstream.secret')
119 'secret': config.get('channelstream.secret')
120 }
120 }
121
121
122 # Limit backends to "vcs.backends" from configuration
122 # Limit backends to "vcs.backends" from configuration
123 backends = config['vcs.backends'] = aslist(
123 backends = config['vcs.backends'] = aslist(
124 config.get('vcs.backends', 'hg,git'), sep=',')
124 config.get('vcs.backends', 'hg,git'), sep=',')
125 for alias in rhodecode.BACKENDS.keys():
125 for alias in rhodecode.BACKENDS.keys():
126 if alias not in backends:
126 if alias not in backends:
127 del rhodecode.BACKENDS[alias]
127 del rhodecode.BACKENDS[alias]
128 log.info("Enabled backends: %s", backends)
128 log.info("Enabled backends: %s", backends)
129
129
130 # initialize vcs client and optionally run the server if enabled
130 # initialize vcs client and optionally run the server if enabled
131 vcs_server_uri = config.get('vcs.server', '')
131 vcs_server_uri = config.get('vcs.server', '')
132 vcs_server_enabled = str2bool(config.get('vcs.server.enable', 'true'))
132 vcs_server_enabled = str2bool(config.get('vcs.server.enable', 'true'))
133 start_server = (
133 start_server = (
134 str2bool(config.get('vcs.start_server', 'false')) and
134 str2bool(config.get('vcs.start_server', 'false')) and
135 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
135 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
136 if vcs_server_enabled and start_server:
136 if vcs_server_enabled and start_server:
137 log.info("Starting vcsserver")
137 log.info("Starting vcsserver")
138 start_vcs_server(server_and_port=vcs_server_uri,
138 start_vcs_server(server_and_port=vcs_server_uri,
139 protocol=utils.get_vcs_server_protocol(config),
139 protocol=utils.get_vcs_server_protocol(config),
140 log_level=config['vcs.server.log_level'])
140 log_level=config['vcs.server.log_level'])
141
141
142 set_available_permissions(config)
142 set_available_permissions(config)
143 db_cfg = make_db_config(clear_session=True)
143 db_cfg = make_db_config(clear_session=True)
144
144
145 repos_path = list(db_cfg.items('paths'))[0][1]
145 repos_path = list(db_cfg.items('paths'))[0][1]
146 config['base_path'] = repos_path
146 config['base_path'] = repos_path
147
147
148 config['vcs.hooks.direct_calls'] = _use_direct_hook_calls(config)
148 config['vcs.hooks.direct_calls'] = _use_direct_hook_calls(config)
149 config['vcs.hooks.protocol'] = _get_vcs_hooks_protocol(config)
149 config['vcs.hooks.protocol'] = _get_vcs_hooks_protocol(config)
150
150
151 # store db config also in main global CONFIG
151 # store db config also in main global CONFIG
152 set_rhodecode_config(config)
152 set_rhodecode_config(config)
153
153
154 # configure instance id
154 # configure instance id
155 utils.set_instance_id(config)
155 utils.set_instance_id(config)
156
156
157 # CONFIGURATION OPTIONS HERE (note: all config options will override
157 # CONFIGURATION OPTIONS HERE (note: all config options will override
158 # any Pylons config options)
158 # any Pylons config options)
159
159
160 # store config reference into our module to skip import magic of pylons
160 # store config reference into our module to skip import magic of pylons
161 rhodecode.CONFIG.update(config)
161 rhodecode.CONFIG.update(config)
162
162
163 utils.configure_pyro4(config)
163 utils.configure_pyro4(config)
164 utils.configure_vcs(config)
164 utils.configure_vcs(config)
165 if vcs_server_enabled:
165 if vcs_server_enabled:
166 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(config))
166 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(config))
167
167
168 import_on_startup = str2bool(config.get('startup.import_repos', False))
169 if vcs_server_enabled and import_on_startup:
170 repo2db_mapper(ScmModel().repo_scan(repos_path), remove_obsolete=False)
171 return config
168 return config
172
169
173
170
174 def _use_direct_hook_calls(config):
171 def _use_direct_hook_calls(config):
175 default_direct_hook_calls = 'false'
172 default_direct_hook_calls = 'false'
176 direct_hook_calls = str2bool(
173 direct_hook_calls = str2bool(
177 config.get('vcs.hooks.direct_calls', default_direct_hook_calls))
174 config.get('vcs.hooks.direct_calls', default_direct_hook_calls))
178 return direct_hook_calls
175 return direct_hook_calls
179
176
180
177
181 def _get_vcs_hooks_protocol(config):
178 def _get_vcs_hooks_protocol(config):
182 protocol = config.get('vcs.hooks.protocol', 'pyro4').lower()
179 protocol = config.get('vcs.hooks.protocol', 'pyro4').lower()
183 return protocol
180 return protocol
184
181
185
182
186 def load_pyramid_environment(global_config, settings):
183 def load_pyramid_environment(global_config, settings):
187 # Some parts of the code expect a merge of global and app settings.
184 # Some parts of the code expect a merge of global and app settings.
188 settings_merged = global_config.copy()
185 settings_merged = global_config.copy()
189 settings_merged.update(settings)
186 settings_merged.update(settings)
190
187
191 # If this is a test run we prepare the test environment like
188 # If this is a test run we prepare the test environment like
192 # creating a test database, test search index and test repositories.
189 # creating a test database, test search index and test repositories.
193 # This has to be done before the database connection is initialized.
190 # This has to be done before the database connection is initialized.
194 if settings['is_test']:
191 if settings['is_test']:
195 rhodecode.is_test = True
192 rhodecode.is_test = True
196 utils.initialize_test_environment(settings_merged)
193 utils.initialize_test_environment(settings_merged)
197
194
198 # Initialize the database connection.
195 # Initialize the database connection.
199 utils.initialize_database(settings_merged)
196 utils.initialize_database(settings_merged)
@@ -1,404 +1,409 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 from collections import OrderedDict
25 from collections import OrderedDict
26
26
27 from paste.registry import RegistryManager
27 from paste.registry import RegistryManager
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 from pylons.wsgiapp import PylonsApp
29 from pylons.wsgiapp import PylonsApp
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.static import static_view
32 from pyramid.static import static_view
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.wsgi import wsgiapp
34 from pyramid.wsgi import wsgiapp
35 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pyramid.events import ApplicationCreated
37 import pyramid.httpexceptions as httpexceptions
38 import pyramid.httpexceptions as httpexceptions
38 from pyramid.renderers import render_to_response, render
39 from pyramid.renderers import render_to_response, render
39 from routes.middleware import RoutesMiddleware
40 from routes.middleware import RoutesMiddleware
40 import routes.util
41 import routes.util
41
42
42 import rhodecode
43 import rhodecode
43 import rhodecode.integrations # do not remove this as it registers celery tasks
44 import rhodecode.integrations # do not remove this as it registers celery tasks
44 from rhodecode.config import patches
45 from rhodecode.config import patches
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
46 from rhodecode.config.routing import STATIC_FILE_PREFIX
46 from rhodecode.config.environment import (
47 from rhodecode.config.environment import (
47 load_environment, load_pyramid_environment)
48 load_environment, load_pyramid_environment)
48 from rhodecode.lib.middleware import csrf
49 from rhodecode.lib.middleware import csrf
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
51 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
55 from rhodecode.subscribers import scan_repositories_if_enabled
54
56
55
57
56 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
57
59
58
60
59 # this is used to avoid avoid the route lookup overhead in routesmiddleware
61 # this is used to avoid avoid the route lookup overhead in routesmiddleware
60 # for certain routes which won't go to pylons to - eg. static files, debugger
62 # for certain routes which won't go to pylons to - eg. static files, debugger
61 # it is only needed for the pylons migration and can be removed once complete
63 # it is only needed for the pylons migration and can be removed once complete
62 class SkippableRoutesMiddleware(RoutesMiddleware):
64 class SkippableRoutesMiddleware(RoutesMiddleware):
63 """ Routes middleware that allows you to skip prefixes """
65 """ Routes middleware that allows you to skip prefixes """
64
66
65 def __init__(self, *args, **kw):
67 def __init__(self, *args, **kw):
66 self.skip_prefixes = kw.pop('skip_prefixes', [])
68 self.skip_prefixes = kw.pop('skip_prefixes', [])
67 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
69 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
68
70
69 def __call__(self, environ, start_response):
71 def __call__(self, environ, start_response):
70 for prefix in self.skip_prefixes:
72 for prefix in self.skip_prefixes:
71 if environ['PATH_INFO'].startswith(prefix):
73 if environ['PATH_INFO'].startswith(prefix):
72 # added to avoid the case when a missing /_static route falls
74 # added to avoid the case when a missing /_static route falls
73 # through to pylons and causes an exception as pylons is
75 # through to pylons and causes an exception as pylons is
74 # expecting wsgiorg.routingargs to be set in the environ
76 # expecting wsgiorg.routingargs to be set in the environ
75 # by RoutesMiddleware.
77 # by RoutesMiddleware.
76 if 'wsgiorg.routing_args' not in environ:
78 if 'wsgiorg.routing_args' not in environ:
77 environ['wsgiorg.routing_args'] = (None, {})
79 environ['wsgiorg.routing_args'] = (None, {})
78 return self.app(environ, start_response)
80 return self.app(environ, start_response)
79
81
80 return super(SkippableRoutesMiddleware, self).__call__(
82 return super(SkippableRoutesMiddleware, self).__call__(
81 environ, start_response)
83 environ, start_response)
82
84
83
85
84 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
86 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
85 """Create a Pylons WSGI application and return it
87 """Create a Pylons WSGI application and return it
86
88
87 ``global_conf``
89 ``global_conf``
88 The inherited configuration for this application. Normally from
90 The inherited configuration for this application. Normally from
89 the [DEFAULT] section of the Paste ini file.
91 the [DEFAULT] section of the Paste ini file.
90
92
91 ``full_stack``
93 ``full_stack``
92 Whether or not this application provides a full WSGI stack (by
94 Whether or not this application provides a full WSGI stack (by
93 default, meaning it handles its own exceptions and errors).
95 default, meaning it handles its own exceptions and errors).
94 Disable full_stack when this application is "managed" by
96 Disable full_stack when this application is "managed" by
95 another WSGI middleware.
97 another WSGI middleware.
96
98
97 ``app_conf``
99 ``app_conf``
98 The application's local configuration. Normally specified in
100 The application's local configuration. Normally specified in
99 the [app:<name>] section of the Paste ini file (where <name>
101 the [app:<name>] section of the Paste ini file (where <name>
100 defaults to main).
102 defaults to main).
101
103
102 """
104 """
103 # Apply compatibility patches
105 # Apply compatibility patches
104 patches.kombu_1_5_1_python_2_7_11()
106 patches.kombu_1_5_1_python_2_7_11()
105 patches.inspect_getargspec()
107 patches.inspect_getargspec()
106
108
107 # Configure the Pylons environment
109 # Configure the Pylons environment
108 config = load_environment(global_conf, app_conf)
110 config = load_environment(global_conf, app_conf)
109
111
110 # The Pylons WSGI app
112 # The Pylons WSGI app
111 app = PylonsApp(config=config)
113 app = PylonsApp(config=config)
112 if rhodecode.is_test:
114 if rhodecode.is_test:
113 app = csrf.CSRFDetector(app)
115 app = csrf.CSRFDetector(app)
114
116
115 expected_origin = config.get('expected_origin')
117 expected_origin = config.get('expected_origin')
116 if expected_origin:
118 if expected_origin:
117 # The API can be accessed from other Origins.
119 # The API can be accessed from other Origins.
118 app = csrf.OriginChecker(app, expected_origin,
120 app = csrf.OriginChecker(app, expected_origin,
119 skip_urls=[routes.util.url_for('api')])
121 skip_urls=[routes.util.url_for('api')])
120
122
121
123
122 if asbool(full_stack):
124 if asbool(full_stack):
123
125
124 # Appenlight monitoring and error handler
126 # Appenlight monitoring and error handler
125 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
127 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
126
128
127 # we want our low level middleware to get to the request ASAP. We don't
129 # we want our low level middleware to get to the request ASAP. We don't
128 # need any pylons stack middleware in them
130 # need any pylons stack middleware in them
129 app = VCSMiddleware(app, config, appenlight_client)
131 app = VCSMiddleware(app, config, appenlight_client)
130
132
131 # Establish the Registry for this application
133 # Establish the Registry for this application
132 app = RegistryManager(app)
134 app = RegistryManager(app)
133
135
134 app.config = config
136 app.config = config
135
137
136 return app
138 return app
137
139
138
140
139 def make_pyramid_app(global_config, **settings):
141 def make_pyramid_app(global_config, **settings):
140 """
142 """
141 Constructs the WSGI application based on Pyramid and wraps the Pylons based
143 Constructs the WSGI application based on Pyramid and wraps the Pylons based
142 application.
144 application.
143
145
144 Specials:
146 Specials:
145
147
146 * We migrate from Pylons to Pyramid. While doing this, we keep both
148 * We migrate from Pylons to Pyramid. While doing this, we keep both
147 frameworks functional. This involves moving some WSGI middlewares around
149 frameworks functional. This involves moving some WSGI middlewares around
148 and providing access to some data internals, so that the old code is
150 and providing access to some data internals, so that the old code is
149 still functional.
151 still functional.
150
152
151 * The application can also be integrated like a plugin via the call to
153 * The application can also be integrated like a plugin via the call to
152 `includeme`. This is accompanied with the other utility functions which
154 `includeme`. This is accompanied with the other utility functions which
153 are called. Changing this should be done with great care to not break
155 are called. Changing this should be done with great care to not break
154 cases when these fragments are assembled from another place.
156 cases when these fragments are assembled from another place.
155
157
156 """
158 """
157 # The edition string should be available in pylons too, so we add it here
159 # The edition string should be available in pylons too, so we add it here
158 # before copying the settings.
160 # before copying the settings.
159 settings.setdefault('rhodecode.edition', 'Community Edition')
161 settings.setdefault('rhodecode.edition', 'Community Edition')
160
162
161 # As long as our Pylons application does expect "unprepared" settings, make
163 # As long as our Pylons application does expect "unprepared" settings, make
162 # sure that we keep an unmodified copy. This avoids unintentional change of
164 # sure that we keep an unmodified copy. This avoids unintentional change of
163 # behavior in the old application.
165 # behavior in the old application.
164 settings_pylons = settings.copy()
166 settings_pylons = settings.copy()
165
167
166 sanitize_settings_and_apply_defaults(settings)
168 sanitize_settings_and_apply_defaults(settings)
167 config = Configurator(settings=settings)
169 config = Configurator(settings=settings)
168 add_pylons_compat_data(config.registry, global_config, settings_pylons)
170 add_pylons_compat_data(config.registry, global_config, settings_pylons)
169
171
170 load_pyramid_environment(global_config, settings)
172 load_pyramid_environment(global_config, settings)
171
173
172 includeme_first(config)
174 includeme_first(config)
173 includeme(config)
175 includeme(config)
174 pyramid_app = config.make_wsgi_app()
176 pyramid_app = config.make_wsgi_app()
175 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
177 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
176 return pyramid_app
178 return pyramid_app
177
179
178
180
179 def add_pylons_compat_data(registry, global_config, settings):
181 def add_pylons_compat_data(registry, global_config, settings):
180 """
182 """
181 Attach data to the registry to support the Pylons integration.
183 Attach data to the registry to support the Pylons integration.
182 """
184 """
183 registry._pylons_compat_global_config = global_config
185 registry._pylons_compat_global_config = global_config
184 registry._pylons_compat_settings = settings
186 registry._pylons_compat_settings = settings
185
187
186
188
187 def webob_to_pyramid_http_response(webob_response):
189 def webob_to_pyramid_http_response(webob_response):
188 ResponseClass = httpexceptions.status_map[webob_response.status_int]
190 ResponseClass = httpexceptions.status_map[webob_response.status_int]
189 pyramid_response = ResponseClass(webob_response.status)
191 pyramid_response = ResponseClass(webob_response.status)
190 pyramid_response.status = webob_response.status
192 pyramid_response.status = webob_response.status
191 pyramid_response.headers.update(webob_response.headers)
193 pyramid_response.headers.update(webob_response.headers)
192 if pyramid_response.headers['content-type'] == 'text/html':
194 if pyramid_response.headers['content-type'] == 'text/html':
193 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
195 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
194 return pyramid_response
196 return pyramid_response
195
197
196
198
197 def error_handler(exception, request):
199 def error_handler(exception, request):
198 # TODO: dan: replace the old pylons error controller with this
200 # TODO: dan: replace the old pylons error controller with this
199 from rhodecode.model.settings import SettingsModel
201 from rhodecode.model.settings import SettingsModel
200 from rhodecode.lib.utils2 import AttributeDict
202 from rhodecode.lib.utils2 import AttributeDict
201
203
202 try:
204 try:
203 rc_config = SettingsModel().get_all_settings()
205 rc_config = SettingsModel().get_all_settings()
204 except Exception:
206 except Exception:
205 log.exception('failed to fetch settings')
207 log.exception('failed to fetch settings')
206 rc_config = {}
208 rc_config = {}
207
209
208 base_response = HTTPInternalServerError()
210 base_response = HTTPInternalServerError()
209 # prefer original exception for the response since it may have headers set
211 # prefer original exception for the response since it may have headers set
210 if isinstance(exception, HTTPError):
212 if isinstance(exception, HTTPError):
211 base_response = exception
213 base_response = exception
212
214
213 c = AttributeDict()
215 c = AttributeDict()
214 c.error_message = base_response.status
216 c.error_message = base_response.status
215 c.error_explanation = base_response.explanation or str(base_response)
217 c.error_explanation = base_response.explanation or str(base_response)
216 c.visual = AttributeDict()
218 c.visual = AttributeDict()
217
219
218 c.visual.rhodecode_support_url = (
220 c.visual.rhodecode_support_url = (
219 request.registry.settings.get('rhodecode_support_url') or
221 request.registry.settings.get('rhodecode_support_url') or
220 request.route_url('rhodecode_support')
222 request.route_url('rhodecode_support')
221 )
223 )
222 c.redirect_time = 0
224 c.redirect_time = 0
223 c.rhodecode_name = rc_config.get('rhodecode_title', '')
225 c.rhodecode_name = rc_config.get('rhodecode_title', '')
224 if not c.rhodecode_name:
226 if not c.rhodecode_name:
225 c.rhodecode_name = 'Rhodecode'
227 c.rhodecode_name = 'Rhodecode'
226
228
227 response = render_to_response(
229 response = render_to_response(
228 '/errors/error_document.html', {'c': c}, request=request,
230 '/errors/error_document.html', {'c': c}, request=request,
229 response=base_response)
231 response=base_response)
230
232
231 return response
233 return response
232
234
233
235
234 def includeme(config):
236 def includeme(config):
235 settings = config.registry.settings
237 settings = config.registry.settings
236
238
237 # plugin information
239 # plugin information
238 config.registry.rhodecode_plugins = OrderedDict()
240 config.registry.rhodecode_plugins = OrderedDict()
239
241
240 config.add_directive(
242 config.add_directive(
241 'register_rhodecode_plugin', register_rhodecode_plugin)
243 'register_rhodecode_plugin', register_rhodecode_plugin)
242
244
243 if asbool(settings.get('appenlight', 'false')):
245 if asbool(settings.get('appenlight', 'false')):
244 config.include('appenlight_client.ext.pyramid_tween')
246 config.include('appenlight_client.ext.pyramid_tween')
245
247
246 # Includes which are required. The application would fail without them.
248 # Includes which are required. The application would fail without them.
247 config.include('pyramid_mako')
249 config.include('pyramid_mako')
248 config.include('pyramid_beaker')
250 config.include('pyramid_beaker')
249 config.include('rhodecode.channelstream')
251 config.include('rhodecode.channelstream')
250 config.include('rhodecode.admin')
252 config.include('rhodecode.admin')
251 config.include('rhodecode.authentication')
253 config.include('rhodecode.authentication')
252 config.include('rhodecode.integrations')
254 config.include('rhodecode.integrations')
253 config.include('rhodecode.login')
255 config.include('rhodecode.login')
254 config.include('rhodecode.tweens')
256 config.include('rhodecode.tweens')
255 config.include('rhodecode.api')
257 config.include('rhodecode.api')
256 config.include('rhodecode.svn_support')
258 config.include('rhodecode.svn_support')
257 config.add_route(
259 config.add_route(
258 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
260 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
259
261
262 # Add subscribers.
263 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
264
260 # Set the authorization policy.
265 # Set the authorization policy.
261 authz_policy = ACLAuthorizationPolicy()
266 authz_policy = ACLAuthorizationPolicy()
262 config.set_authorization_policy(authz_policy)
267 config.set_authorization_policy(authz_policy)
263
268
264 # Set the default renderer for HTML templates to mako.
269 # Set the default renderer for HTML templates to mako.
265 config.add_mako_renderer('.html')
270 config.add_mako_renderer('.html')
266
271
267 # include RhodeCode plugins
272 # include RhodeCode plugins
268 includes = aslist(settings.get('rhodecode.includes', []))
273 includes = aslist(settings.get('rhodecode.includes', []))
269 for inc in includes:
274 for inc in includes:
270 config.include(inc)
275 config.include(inc)
271
276
272 pylons_app = make_app(
277 pylons_app = make_app(
273 config.registry._pylons_compat_global_config,
278 config.registry._pylons_compat_global_config,
274 **config.registry._pylons_compat_settings)
279 **config.registry._pylons_compat_settings)
275 config.registry._pylons_compat_config = pylons_app.config
280 config.registry._pylons_compat_config = pylons_app.config
276
281
277 pylons_app_as_view = wsgiapp(pylons_app)
282 pylons_app_as_view = wsgiapp(pylons_app)
278
283
279 # Protect from VCS Server error related pages when server is not available
284 # Protect from VCS Server error related pages when server is not available
280 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
285 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
281 if not vcs_server_enabled:
286 if not vcs_server_enabled:
282 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
287 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
283
288
284
289
285 def pylons_app_with_error_handler(context, request):
290 def pylons_app_with_error_handler(context, request):
286 """
291 """
287 Handle exceptions from rc pylons app:
292 Handle exceptions from rc pylons app:
288
293
289 - old webob type exceptions get converted to pyramid exceptions
294 - old webob type exceptions get converted to pyramid exceptions
290 - pyramid exceptions are passed to the error handler view
295 - pyramid exceptions are passed to the error handler view
291 """
296 """
292 try:
297 try:
293 response = pylons_app_as_view(context, request)
298 response = pylons_app_as_view(context, request)
294 if 400 <= response.status_int <= 599: # webob type error responses
299 if 400 <= response.status_int <= 599: # webob type error responses
295 return error_handler(
300 return error_handler(
296 webob_to_pyramid_http_response(response), request)
301 webob_to_pyramid_http_response(response), request)
297 except HTTPError as e: # pyramid type exceptions
302 except HTTPError as e: # pyramid type exceptions
298 return error_handler(e, request)
303 return error_handler(e, request)
299 except Exception:
304 except Exception:
300 if settings.get('debugtoolbar.enabled', False):
305 if settings.get('debugtoolbar.enabled', False):
301 raise
306 raise
302 return error_handler(HTTPInternalServerError(), request)
307 return error_handler(HTTPInternalServerError(), request)
303 return response
308 return response
304
309
305 # This is the glue which allows us to migrate in chunks. By registering the
310 # This is the glue which allows us to migrate in chunks. By registering the
306 # pylons based application as the "Not Found" view in Pyramid, we will
311 # pylons based application as the "Not Found" view in Pyramid, we will
307 # fallback to the old application each time the new one does not yet know
312 # fallback to the old application each time the new one does not yet know
308 # how to handle a request.
313 # how to handle a request.
309 config.add_notfound_view(pylons_app_with_error_handler)
314 config.add_notfound_view(pylons_app_with_error_handler)
310
315
311 if not settings.get('debugtoolbar.enabled', False):
316 if not settings.get('debugtoolbar.enabled', False):
312 # if no toolbar, then any exception gets caught and rendered
317 # if no toolbar, then any exception gets caught and rendered
313 config.add_view(error_handler, context=Exception)
318 config.add_view(error_handler, context=Exception)
314
319
315 config.add_view(error_handler, context=HTTPError)
320 config.add_view(error_handler, context=HTTPError)
316
321
317
322
318 def includeme_first(config):
323 def includeme_first(config):
319 # redirect automatic browser favicon.ico requests to correct place
324 # redirect automatic browser favicon.ico requests to correct place
320 def favicon_redirect(context, request):
325 def favicon_redirect(context, request):
321 return redirect(
326 return redirect(
322 request.static_path('rhodecode:public/images/favicon.ico'))
327 request.static_path('rhodecode:public/images/favicon.ico'))
323
328
324 config.add_view(favicon_redirect, route_name='favicon')
329 config.add_view(favicon_redirect, route_name='favicon')
325 config.add_route('favicon', '/favicon.ico')
330 config.add_route('favicon', '/favicon.ico')
326
331
327 config.add_static_view(
332 config.add_static_view(
328 '_static/deform', 'deform:static')
333 '_static/deform', 'deform:static')
329 config.add_static_view(
334 config.add_static_view(
330 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
335 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
331
336
332 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
337 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
333 """
338 """
334 Apply outer WSGI middlewares around the application.
339 Apply outer WSGI middlewares around the application.
335
340
336 Part of this has been moved up from the Pylons layer, so that the
341 Part of this has been moved up from the Pylons layer, so that the
337 data is also available if old Pylons code is hit through an already ported
342 data is also available if old Pylons code is hit through an already ported
338 view.
343 view.
339 """
344 """
340 settings = config.registry.settings
345 settings = config.registry.settings
341
346
342 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
347 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
343 pyramid_app = HttpsFixup(pyramid_app, settings)
348 pyramid_app = HttpsFixup(pyramid_app, settings)
344
349
345 # Add RoutesMiddleware to support the pylons compatibility tween during
350 # Add RoutesMiddleware to support the pylons compatibility tween during
346 # migration to pyramid.
351 # migration to pyramid.
347 pyramid_app = SkippableRoutesMiddleware(
352 pyramid_app = SkippableRoutesMiddleware(
348 pyramid_app, config.registry._pylons_compat_config['routes.map'],
353 pyramid_app, config.registry._pylons_compat_config['routes.map'],
349 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
354 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
350
355
351 if asbool(settings.get('appenlight', 'false')):
356 if asbool(settings.get('appenlight', 'false')):
352 pyramid_app, _ = wrap_in_appenlight_if_enabled(
357 pyramid_app, _ = wrap_in_appenlight_if_enabled(
353 pyramid_app, config.registry._pylons_compat_config)
358 pyramid_app, config.registry._pylons_compat_config)
354
359
355 if asbool(settings.get('gzip_responses', 'true')):
360 if asbool(settings.get('gzip_responses', 'true')):
356 pyramid_app = make_gzip_middleware(
361 pyramid_app = make_gzip_middleware(
357 pyramid_app, settings, compress_level=1)
362 pyramid_app, settings, compress_level=1)
358
363
359 return pyramid_app
364 return pyramid_app
360
365
361
366
362 def sanitize_settings_and_apply_defaults(settings):
367 def sanitize_settings_and_apply_defaults(settings):
363 """
368 """
364 Applies settings defaults and does all type conversion.
369 Applies settings defaults and does all type conversion.
365
370
366 We would move all settings parsing and preparation into this place, so that
371 We would move all settings parsing and preparation into this place, so that
367 we have only one place left which deals with this part. The remaining parts
372 we have only one place left which deals with this part. The remaining parts
368 of the application would start to rely fully on well prepared settings.
373 of the application would start to rely fully on well prepared settings.
369
374
370 This piece would later be split up per topic to avoid a big fat monster
375 This piece would later be split up per topic to avoid a big fat monster
371 function.
376 function.
372 """
377 """
373
378
374 # Pyramid's mako renderer has to search in the templates folder so that the
379 # Pyramid's mako renderer has to search in the templates folder so that the
375 # old templates still work. Ported and new templates are expected to use
380 # old templates still work. Ported and new templates are expected to use
376 # real asset specifications for the includes.
381 # real asset specifications for the includes.
377 mako_directories = settings.setdefault('mako.directories', [
382 mako_directories = settings.setdefault('mako.directories', [
378 # Base templates of the original Pylons application
383 # Base templates of the original Pylons application
379 'rhodecode:templates',
384 'rhodecode:templates',
380 ])
385 ])
381 log.debug(
386 log.debug(
382 "Using the following Mako template directories: %s",
387 "Using the following Mako template directories: %s",
383 mako_directories)
388 mako_directories)
384
389
385 # Default includes, possible to change as a user
390 # Default includes, possible to change as a user
386 pyramid_includes = settings.setdefault('pyramid.includes', [
391 pyramid_includes = settings.setdefault('pyramid.includes', [
387 'rhodecode.lib.middleware.request_wrapper',
392 'rhodecode.lib.middleware.request_wrapper',
388 ])
393 ])
389 log.debug(
394 log.debug(
390 "Using the following pyramid.includes: %s",
395 "Using the following pyramid.includes: %s",
391 pyramid_includes)
396 pyramid_includes)
392
397
393 # TODO: johbo: Re-think this, usually the call to config.include
398 # TODO: johbo: Re-think this, usually the call to config.include
394 # should allow to pass in a prefix.
399 # should allow to pass in a prefix.
395 settings.setdefault('rhodecode.api.url', '/_admin/api')
400 settings.setdefault('rhodecode.api.url', '/_admin/api')
396
401
397 _bool_setting(settings, 'vcs.server.enable', 'true')
402 _bool_setting(settings, 'vcs.server.enable', 'true')
398 _bool_setting(settings, 'is_test', 'false')
403 _bool_setting(settings, 'is_test', 'false')
399
404
400 return settings
405 return settings
401
406
402
407
403 def _bool_setting(settings, name, default):
408 def _bool_setting(settings, name, default):
404 settings[name] = asbool(settings.get(name, default))
409 settings[name] = asbool(settings.get(name, default))
@@ -1,55 +1,70 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pylons
22 import pylons
23
23
24 from pyramid.i18n import get_localizer
24 from pyramid.i18n import get_localizer
25 from pyramid.threadlocal import get_current_request
25 from pyramid.threadlocal import get_current_request
26
26
27 from rhodecode.translation import _ as tsf
27 from rhodecode.translation import _ as tsf
28
28
29
29
30 def add_renderer_globals(event):
30 def add_renderer_globals(event):
31 # Put pylons stuff into the context. This will be removed as soon as
31 # Put pylons stuff into the context. This will be removed as soon as
32 # migration to pyramid is finished.
32 # migration to pyramid is finished.
33 conf = pylons.config._current_obj()
33 conf = pylons.config._current_obj()
34 event['h'] = conf.get('pylons.h')
34 event['h'] = conf.get('pylons.h')
35 event['c'] = pylons.tmpl_context
35 event['c'] = pylons.tmpl_context
36 event['url'] = pylons.url
36 event['url'] = pylons.url
37
37
38 # TODO: When executed in pyramid view context the request is not available
38 # TODO: When executed in pyramid view context the request is not available
39 # in the event. Find a better solution to get the request.
39 # in the event. Find a better solution to get the request.
40 request = event['request'] or get_current_request()
40 request = event['request'] or get_current_request()
41
41
42 # Add Pyramid translation as '_' to context
42 # Add Pyramid translation as '_' to context
43 event['_'] = request.translate
43 event['_'] = request.translate
44 event['localizer'] = request.localizer
44 event['localizer'] = request.localizer
45
45
46
46
47 def add_localizer(event):
47 def add_localizer(event):
48 request = event.request
48 request = event.request
49 localizer = get_localizer(request)
49 localizer = get_localizer(request)
50
50
51 def auto_translate(*args, **kwargs):
51 def auto_translate(*args, **kwargs):
52 return localizer.translate(tsf(*args, **kwargs))
52 return localizer.translate(tsf(*args, **kwargs))
53
53
54 request.localizer = localizer
54 request.localizer = localizer
55 request.translate = auto_translate
55 request.translate = auto_translate
56
57
58 def scan_repositories_if_enabled(event):
59 """
60 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
61 does a repository scan if enabled in the settings.
62 """
63 from rhodecode.model.scm import ScmModel
64 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
65 settings = event.app.registry.settings
66 vcs_server_enabled = settings['vcs.server.enable']
67 import_on_startup = settings['startup.import_repos']
68 if vcs_server_enabled and import_on_startup:
69 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
70 repo2db_mapper(repositories, remove_obsolete=False)
General Comments 0
You need to be logged in to leave comments. Login now