##// END OF EJS Templates
application: moved JS routes generation into pyramid....
marcink -
r1538:5bdec8f6 default
parent child Browse files
Show More
@@ -1,189 +1,180 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 # ------------------------------------------------------------------------------
37 # ------------------------------------------------------------------------------
38 # CELERY magic until refactor - issue #4163 - import order matters here:
38 # CELERY magic until refactor - issue #4163 - import order matters here:
39 from rhodecode.lib import celerypylons # this must be first, celerypylons
39 from rhodecode.lib import celerypylons # this must be first, celerypylons
40 # sets config settings upon import
40 # sets config settings upon import
41
41
42 import rhodecode.integrations # any modules using celery task
42 import rhodecode.integrations # any modules using celery task
43 # decorators should be added afterwards:
43 # decorators should be added afterwards:
44 # ------------------------------------------------------------------------------
44 # ------------------------------------------------------------------------------
45
45
46 from rhodecode.lib import app_globals
46 from rhodecode.lib import app_globals
47 from rhodecode.config import utils
47 from rhodecode.config import utils
48 from rhodecode.config.routing import make_map
48 from rhodecode.config.routing import make_map
49 from rhodecode.config.jsroutes import generate_jsroutes_content
49 from rhodecode.config.jsroutes import generate_jsroutes_content
50
50
51 from rhodecode.lib import helpers
51 from rhodecode.lib import helpers
52 from rhodecode.lib.auth import set_available_permissions
52 from rhodecode.lib.auth import set_available_permissions
53 from rhodecode.lib.utils import (
53 from rhodecode.lib.utils import (
54 repo2db_mapper, make_db_config, set_rhodecode_config,
54 repo2db_mapper, make_db_config, set_rhodecode_config,
55 load_rcextensions)
55 load_rcextensions)
56 from rhodecode.lib.utils2 import str2bool, aslist
56 from rhodecode.lib.utils2 import str2bool, aslist
57 from rhodecode.lib.vcs import connect_vcs, start_vcs_server
57 from rhodecode.lib.vcs import connect_vcs, start_vcs_server
58 from rhodecode.model.scm import ScmModel
58 from rhodecode.model.scm import ScmModel
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62 def load_environment(global_conf, app_conf, initial=False,
62 def load_environment(global_conf, app_conf, initial=False,
63 test_env=None, test_index=None):
63 test_env=None, test_index=None):
64 """
64 """
65 Configure the Pylons environment via the ``pylons.config``
65 Configure the Pylons environment via the ``pylons.config``
66 object
66 object
67 """
67 """
68 config = PylonsConfig()
68 config = PylonsConfig()
69
69
70
70
71 # Pylons paths
71 # Pylons paths
72 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
72 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
73 paths = {
73 paths = {
74 'root': root,
74 'root': root,
75 'controllers': os.path.join(root, 'controllers'),
75 'controllers': os.path.join(root, 'controllers'),
76 'static_files': os.path.join(root, 'public'),
76 'static_files': os.path.join(root, 'public'),
77 'templates': [os.path.join(root, 'templates')],
77 'templates': [os.path.join(root, 'templates')],
78 }
78 }
79
79
80 # Initialize config with the basic options
80 # Initialize config with the basic options
81 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
81 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
82
82
83 # store some globals into rhodecode
83 # store some globals into rhodecode
84 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
84 rhodecode.CELERY_ENABLED = str2bool(config['app_conf'].get('use_celery'))
85 rhodecode.CELERY_EAGER = str2bool(
85 rhodecode.CELERY_EAGER = str2bool(
86 config['app_conf'].get('celery.always.eager'))
86 config['app_conf'].get('celery.always.eager'))
87
87
88 config['routes.map'] = make_map(config)
88 config['routes.map'] = make_map(config)
89
89
90 if asbool(config.get('generate_js_files', 'false')):
91 jsroutes = config['routes.map'].jsroutes()
92 jsroutes_file_content = generate_jsroutes_content(jsroutes)
93 jsroutes_file_path = os.path.join(
94 paths['static_files'], 'js', 'rhodecode', 'routes.js')
95
96 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
97 f.write(jsroutes_file_content)
98
99 config['pylons.app_globals'] = app_globals.Globals(config)
90 config['pylons.app_globals'] = app_globals.Globals(config)
100 config['pylons.h'] = helpers
91 config['pylons.h'] = helpers
101 rhodecode.CONFIG = config
92 rhodecode.CONFIG = config
102
93
103 load_rcextensions(root_path=config['here'])
94 load_rcextensions(root_path=config['here'])
104
95
105 # Setup cache object as early as possible
96 # Setup cache object as early as possible
106 import pylons
97 import pylons
107 pylons.cache._push_object(config['pylons.app_globals'].cache)
98 pylons.cache._push_object(config['pylons.app_globals'].cache)
108
99
109 # Create the Mako TemplateLookup, with the default auto-escaping
100 # Create the Mako TemplateLookup, with the default auto-escaping
110 config['pylons.app_globals'].mako_lookup = TemplateLookup(
101 config['pylons.app_globals'].mako_lookup = TemplateLookup(
111 directories=paths['templates'],
102 directories=paths['templates'],
112 error_handler=handle_mako_error,
103 error_handler=handle_mako_error,
113 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
104 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
114 input_encoding='utf-8', default_filters=['escape'],
105 input_encoding='utf-8', default_filters=['escape'],
115 imports=['from webhelpers.html import escape'])
106 imports=['from webhelpers.html import escape'])
116
107
117 # sets the c attribute access when don't existing attribute are accessed
108 # sets the c attribute access when don't existing attribute are accessed
118 config['pylons.strict_tmpl_context'] = True
109 config['pylons.strict_tmpl_context'] = True
119
110
120 # configure channelstream
111 # configure channelstream
121 config['channelstream_config'] = {
112 config['channelstream_config'] = {
122 'enabled': asbool(config.get('channelstream.enabled', False)),
113 'enabled': asbool(config.get('channelstream.enabled', False)),
123 'server': config.get('channelstream.server'),
114 'server': config.get('channelstream.server'),
124 'secret': config.get('channelstream.secret')
115 'secret': config.get('channelstream.secret')
125 }
116 }
126
117
127 set_available_permissions(config)
118 set_available_permissions(config)
128 db_cfg = make_db_config(clear_session=True)
119 db_cfg = make_db_config(clear_session=True)
129
120
130 repos_path = list(db_cfg.items('paths'))[0][1]
121 repos_path = list(db_cfg.items('paths'))[0][1]
131 config['base_path'] = repos_path
122 config['base_path'] = repos_path
132
123
133 # store db config also in main global CONFIG
124 # store db config also in main global CONFIG
134 set_rhodecode_config(config)
125 set_rhodecode_config(config)
135
126
136 # configure instance id
127 # configure instance id
137 utils.set_instance_id(config)
128 utils.set_instance_id(config)
138
129
139 # CONFIGURATION OPTIONS HERE (note: all config options will override
130 # CONFIGURATION OPTIONS HERE (note: all config options will override
140 # any Pylons config options)
131 # any Pylons config options)
141
132
142 # store config reference into our module to skip import magic of pylons
133 # store config reference into our module to skip import magic of pylons
143 rhodecode.CONFIG.update(config)
134 rhodecode.CONFIG.update(config)
144
135
145 return config
136 return config
146
137
147
138
148 def load_pyramid_environment(global_config, settings):
139 def load_pyramid_environment(global_config, settings):
149 # Some parts of the code expect a merge of global and app settings.
140 # Some parts of the code expect a merge of global and app settings.
150 settings_merged = global_config.copy()
141 settings_merged = global_config.copy()
151 settings_merged.update(settings)
142 settings_merged.update(settings)
152
143
153 # Store the settings to make them available to other modules.
144 # Store the settings to make them available to other modules.
154 rhodecode.PYRAMID_SETTINGS = settings_merged
145 rhodecode.PYRAMID_SETTINGS = settings_merged
155
146
156 # If this is a test run we prepare the test environment like
147 # If this is a test run we prepare the test environment like
157 # creating a test database, test search index and test repositories.
148 # creating a test database, test search index and test repositories.
158 # This has to be done before the database connection is initialized.
149 # This has to be done before the database connection is initialized.
159 if settings['is_test']:
150 if settings['is_test']:
160 rhodecode.is_test = True
151 rhodecode.is_test = True
161 rhodecode.disable_error_handler = True
152 rhodecode.disable_error_handler = True
162
153
163 utils.initialize_test_environment(settings_merged)
154 utils.initialize_test_environment(settings_merged)
164
155
165 # Initialize the database connection.
156 # Initialize the database connection.
166 utils.initialize_database(settings_merged)
157 utils.initialize_database(settings_merged)
167
158
168 # Limit backends to `vcs.backends` from configuration
159 # Limit backends to `vcs.backends` from configuration
169 for alias in rhodecode.BACKENDS.keys():
160 for alias in rhodecode.BACKENDS.keys():
170 if alias not in settings['vcs.backends']:
161 if alias not in settings['vcs.backends']:
171 del rhodecode.BACKENDS[alias]
162 del rhodecode.BACKENDS[alias]
172 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
163 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
173
164
174 # initialize vcs client and optionally run the server if enabled
165 # initialize vcs client and optionally run the server if enabled
175 vcs_server_uri = settings['vcs.server']
166 vcs_server_uri = settings['vcs.server']
176 vcs_server_enabled = settings['vcs.server.enable']
167 vcs_server_enabled = settings['vcs.server.enable']
177 start_server = (
168 start_server = (
178 settings['vcs.start_server'] and
169 settings['vcs.start_server'] and
179 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
170 not int(os.environ.get('RC_VCSSERVER_TEST_DISABLE', '0')))
180
171
181 if vcs_server_enabled and start_server:
172 if vcs_server_enabled and start_server:
182 log.info("Starting vcsserver")
173 log.info("Starting vcsserver")
183 start_vcs_server(server_and_port=vcs_server_uri,
174 start_vcs_server(server_and_port=vcs_server_uri,
184 protocol=utils.get_vcs_server_protocol(settings),
175 protocol=utils.get_vcs_server_protocol(settings),
185 log_level=settings['vcs.server.log_level'])
176 log_level=settings['vcs.server.log_level'])
186
177
187 utils.configure_vcs(settings)
178 utils.configure_vcs(settings)
188 if vcs_server_enabled:
179 if vcs_server_enabled:
189 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
180 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
@@ -1,502 +1,504 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 from pyramid.events import ApplicationCreated
36 from pyramid.events import ApplicationCreated
37 from pyramid.renderers import render_to_response
37 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42 from rhodecode.model import meta
42 from rhodecode.model import meta
43 from rhodecode.config import patches
43 from rhodecode.config import patches
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.middleware import csrf
47 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.error_handling import (
49 from rhodecode.lib.middleware.error_handling import (
50 PylonsErrorHandlingMiddleware)
50 PylonsErrorHandlingMiddleware)
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
55 from rhodecode.subscribers import (
55 from rhodecode.subscribers import (
56 scan_repositories_if_enabled, write_metadata_if_needed)
56 scan_repositories_if_enabled, write_metadata_if_needed,
57 write_js_routes_if_enabled)
57
58
58
59
59 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
60
61
61
62
62 # this is used to avoid avoid the route lookup overhead in routesmiddleware
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
63 # for certain routes which won't go to pylons to - eg. static files, debugger
64 # for certain routes which won't go to pylons to - eg. static files, debugger
64 # it is only needed for the pylons migration and can be removed once complete
65 # it is only needed for the pylons migration and can be removed once complete
65 class SkippableRoutesMiddleware(RoutesMiddleware):
66 class SkippableRoutesMiddleware(RoutesMiddleware):
66 """ Routes middleware that allows you to skip prefixes """
67 """ Routes middleware that allows you to skip prefixes """
67
68
68 def __init__(self, *args, **kw):
69 def __init__(self, *args, **kw):
69 self.skip_prefixes = kw.pop('skip_prefixes', [])
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
70 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
71
72
72 def __call__(self, environ, start_response):
73 def __call__(self, environ, start_response):
73 for prefix in self.skip_prefixes:
74 for prefix in self.skip_prefixes:
74 if environ['PATH_INFO'].startswith(prefix):
75 if environ['PATH_INFO'].startswith(prefix):
75 # added to avoid the case when a missing /_static route falls
76 # added to avoid the case when a missing /_static route falls
76 # through to pylons and causes an exception as pylons is
77 # through to pylons and causes an exception as pylons is
77 # expecting wsgiorg.routingargs to be set in the environ
78 # expecting wsgiorg.routingargs to be set in the environ
78 # by RoutesMiddleware.
79 # by RoutesMiddleware.
79 if 'wsgiorg.routing_args' not in environ:
80 if 'wsgiorg.routing_args' not in environ:
80 environ['wsgiorg.routing_args'] = (None, {})
81 environ['wsgiorg.routing_args'] = (None, {})
81 return self.app(environ, start_response)
82 return self.app(environ, start_response)
82
83
83 return super(SkippableRoutesMiddleware, self).__call__(
84 return super(SkippableRoutesMiddleware, self).__call__(
84 environ, start_response)
85 environ, start_response)
85
86
86
87
87 def make_app(global_conf, static_files=True, **app_conf):
88 def make_app(global_conf, static_files=True, **app_conf):
88 """Create a Pylons WSGI application and return it
89 """Create a Pylons WSGI application and return it
89
90
90 ``global_conf``
91 ``global_conf``
91 The inherited configuration for this application. Normally from
92 The inherited configuration for this application. Normally from
92 the [DEFAULT] section of the Paste ini file.
93 the [DEFAULT] section of the Paste ini file.
93
94
94 ``app_conf``
95 ``app_conf``
95 The application's local configuration. Normally specified in
96 The application's local configuration. Normally specified in
96 the [app:<name>] section of the Paste ini file (where <name>
97 the [app:<name>] section of the Paste ini file (where <name>
97 defaults to main).
98 defaults to main).
98
99
99 """
100 """
100 # Apply compatibility patches
101 # Apply compatibility patches
101 patches.kombu_1_5_1_python_2_7_11()
102 patches.kombu_1_5_1_python_2_7_11()
102 patches.inspect_getargspec()
103 patches.inspect_getargspec()
103
104
104 # Configure the Pylons environment
105 # Configure the Pylons environment
105 config = load_environment(global_conf, app_conf)
106 config = load_environment(global_conf, app_conf)
106
107
107 # The Pylons WSGI app
108 # The Pylons WSGI app
108 app = PylonsApp(config=config)
109 app = PylonsApp(config=config)
109 if rhodecode.is_test:
110 if rhodecode.is_test:
110 app = csrf.CSRFDetector(app)
111 app = csrf.CSRFDetector(app)
111
112
112 expected_origin = config.get('expected_origin')
113 expected_origin = config.get('expected_origin')
113 if expected_origin:
114 if expected_origin:
114 # The API can be accessed from other Origins.
115 # The API can be accessed from other Origins.
115 app = csrf.OriginChecker(app, expected_origin,
116 app = csrf.OriginChecker(app, expected_origin,
116 skip_urls=[routes.util.url_for('api')])
117 skip_urls=[routes.util.url_for('api')])
117
118
118 # Establish the Registry for this application
119 # Establish the Registry for this application
119 app = RegistryManager(app)
120 app = RegistryManager(app)
120
121
121 app.config = config
122 app.config = config
122
123
123 return app
124 return app
124
125
125
126
126 def make_pyramid_app(global_config, **settings):
127 def make_pyramid_app(global_config, **settings):
127 """
128 """
128 Constructs the WSGI application based on Pyramid and wraps the Pylons based
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
129 application.
130 application.
130
131
131 Specials:
132 Specials:
132
133
133 * We migrate from Pylons to Pyramid. While doing this, we keep both
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
134 frameworks functional. This involves moving some WSGI middlewares around
135 frameworks functional. This involves moving some WSGI middlewares around
135 and providing access to some data internals, so that the old code is
136 and providing access to some data internals, so that the old code is
136 still functional.
137 still functional.
137
138
138 * The application can also be integrated like a plugin via the call to
139 * The application can also be integrated like a plugin via the call to
139 `includeme`. This is accompanied with the other utility functions which
140 `includeme`. This is accompanied with the other utility functions which
140 are called. Changing this should be done with great care to not break
141 are called. Changing this should be done with great care to not break
141 cases when these fragments are assembled from another place.
142 cases when these fragments are assembled from another place.
142
143
143 """
144 """
144 # The edition string should be available in pylons too, so we add it here
145 # The edition string should be available in pylons too, so we add it here
145 # before copying the settings.
146 # before copying the settings.
146 settings.setdefault('rhodecode.edition', 'Community Edition')
147 settings.setdefault('rhodecode.edition', 'Community Edition')
147
148
148 # As long as our Pylons application does expect "unprepared" settings, make
149 # As long as our Pylons application does expect "unprepared" settings, make
149 # sure that we keep an unmodified copy. This avoids unintentional change of
150 # sure that we keep an unmodified copy. This avoids unintentional change of
150 # behavior in the old application.
151 # behavior in the old application.
151 settings_pylons = settings.copy()
152 settings_pylons = settings.copy()
152
153
153 sanitize_settings_and_apply_defaults(settings)
154 sanitize_settings_and_apply_defaults(settings)
154 config = Configurator(settings=settings)
155 config = Configurator(settings=settings)
155 add_pylons_compat_data(config.registry, global_config, settings_pylons)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
156
157
157 load_pyramid_environment(global_config, settings)
158 load_pyramid_environment(global_config, settings)
158
159
159 includeme_first(config)
160 includeme_first(config)
160 includeme(config)
161 includeme(config)
161 pyramid_app = config.make_wsgi_app()
162 pyramid_app = config.make_wsgi_app()
162 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
163 pyramid_app.config = config
164 pyramid_app.config = config
164
165
165 # creating the app uses a connection - return it after we are done
166 # creating the app uses a connection - return it after we are done
166 meta.Session.remove()
167 meta.Session.remove()
167
168
168 return pyramid_app
169 return pyramid_app
169
170
170
171
171 def make_not_found_view(config):
172 def make_not_found_view(config):
172 """
173 """
173 This creates the view which should be registered as not-found-view to
174 This creates the view which should be registered as not-found-view to
174 pyramid. Basically it contains of the old pylons app, converted to a view.
175 pyramid. Basically it contains of the old pylons app, converted to a view.
175 Additionally it is wrapped by some other middlewares.
176 Additionally it is wrapped by some other middlewares.
176 """
177 """
177 settings = config.registry.settings
178 settings = config.registry.settings
178 vcs_server_enabled = settings['vcs.server.enable']
179 vcs_server_enabled = settings['vcs.server.enable']
179
180
180 # Make pylons app from unprepared settings.
181 # Make pylons app from unprepared settings.
181 pylons_app = make_app(
182 pylons_app = make_app(
182 config.registry._pylons_compat_global_config,
183 config.registry._pylons_compat_global_config,
183 **config.registry._pylons_compat_settings)
184 **config.registry._pylons_compat_settings)
184 config.registry._pylons_compat_config = pylons_app.config
185 config.registry._pylons_compat_config = pylons_app.config
185
186
186 # Appenlight monitoring.
187 # Appenlight monitoring.
187 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
188 pylons_app, settings)
189 pylons_app, settings)
189
190
190 # The pylons app is executed inside of the pyramid 404 exception handler.
191 # The pylons app is executed inside of the pyramid 404 exception handler.
191 # Exceptions which are raised inside of it are not handled by pyramid
192 # Exceptions which are raised inside of it are not handled by pyramid
192 # again. Therefore we add a middleware that invokes the error handler in
193 # again. Therefore we add a middleware that invokes the error handler in
193 # case of an exception or error response. This way we return proper error
194 # case of an exception or error response. This way we return proper error
194 # HTML pages in case of an error.
195 # HTML pages in case of an error.
195 reraise = (settings.get('debugtoolbar.enabled', False) or
196 reraise = (settings.get('debugtoolbar.enabled', False) or
196 rhodecode.disable_error_handler)
197 rhodecode.disable_error_handler)
197 pylons_app = PylonsErrorHandlingMiddleware(
198 pylons_app = PylonsErrorHandlingMiddleware(
198 pylons_app, error_handler, reraise)
199 pylons_app, error_handler, reraise)
199
200
200 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
201 # view to handle the request. Therefore it is wrapped around the pylons
202 # view to handle the request. Therefore it is wrapped around the pylons
202 # app. It has to be outside of the error handling otherwise error responses
203 # app. It has to be outside of the error handling otherwise error responses
203 # from the vcsserver are converted to HTML error pages. This confuses the
204 # from the vcsserver are converted to HTML error pages. This confuses the
204 # command line tools and the user won't get a meaningful error message.
205 # command line tools and the user won't get a meaningful error message.
205 if vcs_server_enabled:
206 if vcs_server_enabled:
206 pylons_app = VCSMiddleware(
207 pylons_app = VCSMiddleware(
207 pylons_app, settings, appenlight_client, registry=config.registry)
208 pylons_app, settings, appenlight_client, registry=config.registry)
208
209
209 # Convert WSGI app to pyramid view and return it.
210 # Convert WSGI app to pyramid view and return it.
210 return wsgiapp(pylons_app)
211 return wsgiapp(pylons_app)
211
212
212
213
213 def add_pylons_compat_data(registry, global_config, settings):
214 def add_pylons_compat_data(registry, global_config, settings):
214 """
215 """
215 Attach data to the registry to support the Pylons integration.
216 Attach data to the registry to support the Pylons integration.
216 """
217 """
217 registry._pylons_compat_global_config = global_config
218 registry._pylons_compat_global_config = global_config
218 registry._pylons_compat_settings = settings
219 registry._pylons_compat_settings = settings
219
220
220
221
221 def error_handler(exception, request):
222 def error_handler(exception, request):
222 import rhodecode
223 import rhodecode
223 from rhodecode.lib.utils2 import AttributeDict
224 from rhodecode.lib.utils2 import AttributeDict
224
225
225 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
226
227
227 base_response = HTTPInternalServerError()
228 base_response = HTTPInternalServerError()
228 # prefer original exception for the response since it may have headers set
229 # prefer original exception for the response since it may have headers set
229 if isinstance(exception, HTTPException):
230 if isinstance(exception, HTTPException):
230 base_response = exception
231 base_response = exception
231
232
232 def is_http_error(response):
233 def is_http_error(response):
233 # error which should have traceback
234 # error which should have traceback
234 return response.status_code > 499
235 return response.status_code > 499
235
236
236 if is_http_error(base_response):
237 if is_http_error(base_response):
237 log.exception(
238 log.exception(
238 'error occurred handling this request for path: %s', request.path)
239 'error occurred handling this request for path: %s', request.path)
239
240
240 c = AttributeDict()
241 c = AttributeDict()
241 c.error_message = base_response.status
242 c.error_message = base_response.status
242 c.error_explanation = base_response.explanation or str(base_response)
243 c.error_explanation = base_response.explanation or str(base_response)
243 c.visual = AttributeDict()
244 c.visual = AttributeDict()
244
245
245 c.visual.rhodecode_support_url = (
246 c.visual.rhodecode_support_url = (
246 request.registry.settings.get('rhodecode_support_url') or
247 request.registry.settings.get('rhodecode_support_url') or
247 request.route_url('rhodecode_support')
248 request.route_url('rhodecode_support')
248 )
249 )
249 c.redirect_time = 0
250 c.redirect_time = 0
250 c.rhodecode_name = rhodecode_title
251 c.rhodecode_name = rhodecode_title
251 if not c.rhodecode_name:
252 if not c.rhodecode_name:
252 c.rhodecode_name = 'Rhodecode'
253 c.rhodecode_name = 'Rhodecode'
253
254
254 c.causes = []
255 c.causes = []
255 if hasattr(base_response, 'causes'):
256 if hasattr(base_response, 'causes'):
256 c.causes = base_response.causes
257 c.causes = base_response.causes
257
258
258 response = render_to_response(
259 response = render_to_response(
259 '/errors/error_document.mako', {'c': c}, request=request,
260 '/errors/error_document.mako', {'c': c}, request=request,
260 response=base_response)
261 response=base_response)
261
262
262 return response
263 return response
263
264
264
265
265 def includeme(config):
266 def includeme(config):
266 settings = config.registry.settings
267 settings = config.registry.settings
267
268
268 # plugin information
269 # plugin information
269 config.registry.rhodecode_plugins = OrderedDict()
270 config.registry.rhodecode_plugins = OrderedDict()
270
271
271 config.add_directive(
272 config.add_directive(
272 'register_rhodecode_plugin', register_rhodecode_plugin)
273 'register_rhodecode_plugin', register_rhodecode_plugin)
273
274
274 if asbool(settings.get('appenlight', 'false')):
275 if asbool(settings.get('appenlight', 'false')):
275 config.include('appenlight_client.ext.pyramid_tween')
276 config.include('appenlight_client.ext.pyramid_tween')
276
277
277 # Includes which are required. The application would fail without them.
278 # Includes which are required. The application would fail without them.
278 config.include('pyramid_mako')
279 config.include('pyramid_mako')
279 config.include('pyramid_beaker')
280 config.include('pyramid_beaker')
280
281
281 config.include('rhodecode.authentication')
282 config.include('rhodecode.authentication')
282 config.include('rhodecode.integrations')
283 config.include('rhodecode.integrations')
283
284
284 # apps
285 # apps
285 config.include('rhodecode.apps.admin')
286 config.include('rhodecode.apps.admin')
286 config.include('rhodecode.apps.channelstream')
287 config.include('rhodecode.apps.channelstream')
287 config.include('rhodecode.apps.login')
288 config.include('rhodecode.apps.login')
288 config.include('rhodecode.apps.user_profile')
289 config.include('rhodecode.apps.user_profile')
289 config.include('rhodecode.apps.my_account')
290 config.include('rhodecode.apps.my_account')
290 config.include('rhodecode.apps.svn_support')
291 config.include('rhodecode.apps.svn_support')
291
292
292 config.include('rhodecode.tweens')
293 config.include('rhodecode.tweens')
293 config.include('rhodecode.api')
294 config.include('rhodecode.api')
294
295
295 config.add_route(
296 config.add_route(
296 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
297 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
297
298
298 config.add_translation_dirs('rhodecode:i18n/')
299 config.add_translation_dirs('rhodecode:i18n/')
299 settings['default_locale_name'] = settings.get('lang', 'en')
300 settings['default_locale_name'] = settings.get('lang', 'en')
300
301
301 # Add subscribers.
302 # Add subscribers.
302 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
303 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
303 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
304 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
305 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
304
306
305 # Set the authorization policy.
307 # Set the authorization policy.
306 authz_policy = ACLAuthorizationPolicy()
308 authz_policy = ACLAuthorizationPolicy()
307 config.set_authorization_policy(authz_policy)
309 config.set_authorization_policy(authz_policy)
308
310
309 # Set the default renderer for HTML templates to mako.
311 # Set the default renderer for HTML templates to mako.
310 config.add_mako_renderer('.html')
312 config.add_mako_renderer('.html')
311
313
312 # include RhodeCode plugins
314 # include RhodeCode plugins
313 includes = aslist(settings.get('rhodecode.includes', []))
315 includes = aslist(settings.get('rhodecode.includes', []))
314 for inc in includes:
316 for inc in includes:
315 config.include(inc)
317 config.include(inc)
316
318
317 # This is the glue which allows us to migrate in chunks. By registering the
319 # This is the glue which allows us to migrate in chunks. By registering the
318 # pylons based application as the "Not Found" view in Pyramid, we will
320 # pylons based application as the "Not Found" view in Pyramid, we will
319 # fallback to the old application each time the new one does not yet know
321 # fallback to the old application each time the new one does not yet know
320 # how to handle a request.
322 # how to handle a request.
321 config.add_notfound_view(make_not_found_view(config))
323 config.add_notfound_view(make_not_found_view(config))
322
324
323 if not settings.get('debugtoolbar.enabled', False):
325 if not settings.get('debugtoolbar.enabled', False):
324 # if no toolbar, then any exception gets caught and rendered
326 # if no toolbar, then any exception gets caught and rendered
325 config.add_view(error_handler, context=Exception)
327 config.add_view(error_handler, context=Exception)
326
328
327 config.add_view(error_handler, context=HTTPError)
329 config.add_view(error_handler, context=HTTPError)
328
330
329
331
330 def includeme_first(config):
332 def includeme_first(config):
331 # redirect automatic browser favicon.ico requests to correct place
333 # redirect automatic browser favicon.ico requests to correct place
332 def favicon_redirect(context, request):
334 def favicon_redirect(context, request):
333 return HTTPFound(
335 return HTTPFound(
334 request.static_path('rhodecode:public/images/favicon.ico'))
336 request.static_path('rhodecode:public/images/favicon.ico'))
335
337
336 config.add_view(favicon_redirect, route_name='favicon')
338 config.add_view(favicon_redirect, route_name='favicon')
337 config.add_route('favicon', '/favicon.ico')
339 config.add_route('favicon', '/favicon.ico')
338
340
339 def robots_redirect(context, request):
341 def robots_redirect(context, request):
340 return HTTPFound(
342 return HTTPFound(
341 request.static_path('rhodecode:public/robots.txt'))
343 request.static_path('rhodecode:public/robots.txt'))
342
344
343 config.add_view(robots_redirect, route_name='robots')
345 config.add_view(robots_redirect, route_name='robots')
344 config.add_route('robots', '/robots.txt')
346 config.add_route('robots', '/robots.txt')
345
347
346 config.add_static_view(
348 config.add_static_view(
347 '_static/deform', 'deform:static')
349 '_static/deform', 'deform:static')
348 config.add_static_view(
350 config.add_static_view(
349 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
351 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
350
352
351
353
352 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
354 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
353 """
355 """
354 Apply outer WSGI middlewares around the application.
356 Apply outer WSGI middlewares around the application.
355
357
356 Part of this has been moved up from the Pylons layer, so that the
358 Part of this has been moved up from the Pylons layer, so that the
357 data is also available if old Pylons code is hit through an already ported
359 data is also available if old Pylons code is hit through an already ported
358 view.
360 view.
359 """
361 """
360 settings = config.registry.settings
362 settings = config.registry.settings
361
363
362 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
364 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
363 pyramid_app = HttpsFixup(pyramid_app, settings)
365 pyramid_app = HttpsFixup(pyramid_app, settings)
364
366
365 # Add RoutesMiddleware to support the pylons compatibility tween during
367 # Add RoutesMiddleware to support the pylons compatibility tween during
366 # migration to pyramid.
368 # migration to pyramid.
367 pyramid_app = SkippableRoutesMiddleware(
369 pyramid_app = SkippableRoutesMiddleware(
368 pyramid_app, config.registry._pylons_compat_config['routes.map'],
370 pyramid_app, config.registry._pylons_compat_config['routes.map'],
369 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
371 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
370
372
371 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
373 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
372
374
373 if settings['gzip_responses']:
375 if settings['gzip_responses']:
374 pyramid_app = make_gzip_middleware(
376 pyramid_app = make_gzip_middleware(
375 pyramid_app, settings, compress_level=1)
377 pyramid_app, settings, compress_level=1)
376
378
377 # this should be the outer most middleware in the wsgi stack since
379 # this should be the outer most middleware in the wsgi stack since
378 # middleware like Routes make database calls
380 # middleware like Routes make database calls
379 def pyramid_app_with_cleanup(environ, start_response):
381 def pyramid_app_with_cleanup(environ, start_response):
380 try:
382 try:
381 return pyramid_app(environ, start_response)
383 return pyramid_app(environ, start_response)
382 finally:
384 finally:
383 # Dispose current database session and rollback uncommitted
385 # Dispose current database session and rollback uncommitted
384 # transactions.
386 # transactions.
385 meta.Session.remove()
387 meta.Session.remove()
386
388
387 # In a single threaded mode server, on non sqlite db we should have
389 # In a single threaded mode server, on non sqlite db we should have
388 # '0 Current Checked out connections' at the end of a request,
390 # '0 Current Checked out connections' at the end of a request,
389 # if not, then something, somewhere is leaving a connection open
391 # if not, then something, somewhere is leaving a connection open
390 pool = meta.Base.metadata.bind.engine.pool
392 pool = meta.Base.metadata.bind.engine.pool
391 log.debug('sa pool status: %s', pool.status())
393 log.debug('sa pool status: %s', pool.status())
392
394
393
395
394 return pyramid_app_with_cleanup
396 return pyramid_app_with_cleanup
395
397
396
398
397 def sanitize_settings_and_apply_defaults(settings):
399 def sanitize_settings_and_apply_defaults(settings):
398 """
400 """
399 Applies settings defaults and does all type conversion.
401 Applies settings defaults and does all type conversion.
400
402
401 We would move all settings parsing and preparation into this place, so that
403 We would move all settings parsing and preparation into this place, so that
402 we have only one place left which deals with this part. The remaining parts
404 we have only one place left which deals with this part. The remaining parts
403 of the application would start to rely fully on well prepared settings.
405 of the application would start to rely fully on well prepared settings.
404
406
405 This piece would later be split up per topic to avoid a big fat monster
407 This piece would later be split up per topic to avoid a big fat monster
406 function.
408 function.
407 """
409 """
408
410
409 # Pyramid's mako renderer has to search in the templates folder so that the
411 # Pyramid's mako renderer has to search in the templates folder so that the
410 # old templates still work. Ported and new templates are expected to use
412 # old templates still work. Ported and new templates are expected to use
411 # real asset specifications for the includes.
413 # real asset specifications for the includes.
412 mako_directories = settings.setdefault('mako.directories', [
414 mako_directories = settings.setdefault('mako.directories', [
413 # Base templates of the original Pylons application
415 # Base templates of the original Pylons application
414 'rhodecode:templates',
416 'rhodecode:templates',
415 ])
417 ])
416 log.debug(
418 log.debug(
417 "Using the following Mako template directories: %s",
419 "Using the following Mako template directories: %s",
418 mako_directories)
420 mako_directories)
419
421
420 # Default includes, possible to change as a user
422 # Default includes, possible to change as a user
421 pyramid_includes = settings.setdefault('pyramid.includes', [
423 pyramid_includes = settings.setdefault('pyramid.includes', [
422 'rhodecode.lib.middleware.request_wrapper',
424 'rhodecode.lib.middleware.request_wrapper',
423 ])
425 ])
424 log.debug(
426 log.debug(
425 "Using the following pyramid.includes: %s",
427 "Using the following pyramid.includes: %s",
426 pyramid_includes)
428 pyramid_includes)
427
429
428 # TODO: johbo: Re-think this, usually the call to config.include
430 # TODO: johbo: Re-think this, usually the call to config.include
429 # should allow to pass in a prefix.
431 # should allow to pass in a prefix.
430 settings.setdefault('rhodecode.api.url', '/_admin/api')
432 settings.setdefault('rhodecode.api.url', '/_admin/api')
431
433
432 # Sanitize generic settings.
434 # Sanitize generic settings.
433 _list_setting(settings, 'default_encoding', 'UTF-8')
435 _list_setting(settings, 'default_encoding', 'UTF-8')
434 _bool_setting(settings, 'is_test', 'false')
436 _bool_setting(settings, 'is_test', 'false')
435 _bool_setting(settings, 'gzip_responses', 'false')
437 _bool_setting(settings, 'gzip_responses', 'false')
436
438
437 # Call split out functions that sanitize settings for each topic.
439 # Call split out functions that sanitize settings for each topic.
438 _sanitize_appenlight_settings(settings)
440 _sanitize_appenlight_settings(settings)
439 _sanitize_vcs_settings(settings)
441 _sanitize_vcs_settings(settings)
440
442
441 return settings
443 return settings
442
444
443
445
444 def _sanitize_appenlight_settings(settings):
446 def _sanitize_appenlight_settings(settings):
445 _bool_setting(settings, 'appenlight', 'false')
447 _bool_setting(settings, 'appenlight', 'false')
446
448
447
449
448 def _sanitize_vcs_settings(settings):
450 def _sanitize_vcs_settings(settings):
449 """
451 """
450 Applies settings defaults and does type conversion for all VCS related
452 Applies settings defaults and does type conversion for all VCS related
451 settings.
453 settings.
452 """
454 """
453 _string_setting(settings, 'vcs.svn.compatible_version', '')
455 _string_setting(settings, 'vcs.svn.compatible_version', '')
454 _string_setting(settings, 'git_rev_filter', '--all')
456 _string_setting(settings, 'git_rev_filter', '--all')
455 _string_setting(settings, 'vcs.hooks.protocol', 'http')
457 _string_setting(settings, 'vcs.hooks.protocol', 'http')
456 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
458 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
457 _string_setting(settings, 'vcs.server', '')
459 _string_setting(settings, 'vcs.server', '')
458 _string_setting(settings, 'vcs.server.log_level', 'debug')
460 _string_setting(settings, 'vcs.server.log_level', 'debug')
459 _string_setting(settings, 'vcs.server.protocol', 'http')
461 _string_setting(settings, 'vcs.server.protocol', 'http')
460 _bool_setting(settings, 'startup.import_repos', 'false')
462 _bool_setting(settings, 'startup.import_repos', 'false')
461 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
463 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
462 _bool_setting(settings, 'vcs.server.enable', 'true')
464 _bool_setting(settings, 'vcs.server.enable', 'true')
463 _bool_setting(settings, 'vcs.start_server', 'false')
465 _bool_setting(settings, 'vcs.start_server', 'false')
464 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
466 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
465 _int_setting(settings, 'vcs.connection_timeout', 3600)
467 _int_setting(settings, 'vcs.connection_timeout', 3600)
466
468
467 # Support legacy values of vcs.scm_app_implementation. Legacy
469 # Support legacy values of vcs.scm_app_implementation. Legacy
468 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
470 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
469 # which is now mapped to 'http'.
471 # which is now mapped to 'http'.
470 scm_app_impl = settings['vcs.scm_app_implementation']
472 scm_app_impl = settings['vcs.scm_app_implementation']
471 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
473 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
472 settings['vcs.scm_app_implementation'] = 'http'
474 settings['vcs.scm_app_implementation'] = 'http'
473
475
474
476
475 def _int_setting(settings, name, default):
477 def _int_setting(settings, name, default):
476 settings[name] = int(settings.get(name, default))
478 settings[name] = int(settings.get(name, default))
477
479
478
480
479 def _bool_setting(settings, name, default):
481 def _bool_setting(settings, name, default):
480 input = settings.get(name, default)
482 input = settings.get(name, default)
481 if isinstance(input, unicode):
483 if isinstance(input, unicode):
482 input = input.encode('utf8')
484 input = input.encode('utf8')
483 settings[name] = asbool(input)
485 settings[name] = asbool(input)
484
486
485
487
486 def _list_setting(settings, name, default):
488 def _list_setting(settings, name, default):
487 raw_value = settings.get(name, default)
489 raw_value = settings.get(name, default)
488
490
489 old_separator = ','
491 old_separator = ','
490 if old_separator in raw_value:
492 if old_separator in raw_value:
491 # If we get a comma separated list, pass it to our own function.
493 # If we get a comma separated list, pass it to our own function.
492 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
494 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
493 else:
495 else:
494 # Otherwise we assume it uses pyramids space/newline separation.
496 # Otherwise we assume it uses pyramids space/newline separation.
495 settings[name] = aslist(raw_value)
497 settings[name] = aslist(raw_value)
496
498
497
499
498 def _string_setting(settings, name, default, lower=True):
500 def _string_setting(settings, name, default, lower=True):
499 value = settings.get(name, default)
501 value = settings.get(name, default)
500 if lower:
502 if lower:
501 value = value.lower()
503 value = value.lower()
502 settings[name] = value
504 settings[name] = value
@@ -1,252 +1,308 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 import io
21 import re
21 import datetime
22 import datetime
22 import logging
23 import logging
23 import pylons
24 import pylons
24 import Queue
25 import Queue
25 import subprocess32
26 import subprocess32
26 import os
27 import os
27
28
28 from pyramid.i18n import get_localizer
29 from pyramid.i18n import get_localizer
29 from pyramid.threadlocal import get_current_request
30 from pyramid.threadlocal import get_current_request
31 from pyramid.interfaces import IRoutesMapper
32 from pyramid.settings import asbool
33 from pyramid.path import AssetResolver
30 from threading import Thread
34 from threading import Thread
31
35
32 from rhodecode.translation import _ as tsf
36 from rhodecode.translation import _ as tsf
37 from rhodecode.config.jsroutes import generate_jsroutes_content
33
38
34 import rhodecode
39 import rhodecode
35
40
36 from pylons.i18n.translation import _get_translator
41 from pylons.i18n.translation import _get_translator
37 from pylons.util import ContextObj
42 from pylons.util import ContextObj
38 from routes.util import URLGenerator
43 from routes.util import URLGenerator
39
44
40 from rhodecode.lib.base import attach_context_attributes, get_auth_user
45 from rhodecode.lib.base import attach_context_attributes, get_auth_user
41
46
42 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
43
48
44
49
45 def add_renderer_globals(event):
50 def add_renderer_globals(event):
46 # Put pylons stuff into the context. This will be removed as soon as
51 # Put pylons stuff into the context. This will be removed as soon as
47 # migration to pyramid is finished.
52 # migration to pyramid is finished.
48 conf = pylons.config._current_obj()
53 conf = pylons.config._current_obj()
49 event['h'] = conf.get('pylons.h')
54 event['h'] = conf.get('pylons.h')
50 event['c'] = pylons.tmpl_context
55 event['c'] = pylons.tmpl_context
51 event['url'] = pylons.url
56 event['url'] = pylons.url
52
57
53 # TODO: When executed in pyramid view context the request is not available
58 # TODO: When executed in pyramid view context the request is not available
54 # in the event. Find a better solution to get the request.
59 # in the event. Find a better solution to get the request.
55 request = event['request'] or get_current_request()
60 request = event['request'] or get_current_request()
56
61
57 # Add Pyramid translation as '_' to context
62 # Add Pyramid translation as '_' to context
58 event['_'] = request.translate
63 event['_'] = request.translate
59 event['_ungettext'] = request.plularize
64 event['_ungettext'] = request.plularize
60
65
61
66
62 def add_localizer(event):
67 def add_localizer(event):
63 request = event.request
68 request = event.request
64 localizer = get_localizer(request)
69 localizer = get_localizer(request)
65
70
66 def auto_translate(*args, **kwargs):
71 def auto_translate(*args, **kwargs):
67 return localizer.translate(tsf(*args, **kwargs))
72 return localizer.translate(tsf(*args, **kwargs))
68
73
69 request.localizer = localizer
74 request.localizer = localizer
70 request.translate = auto_translate
75 request.translate = auto_translate
71 request.plularize = localizer.pluralize
76 request.plularize = localizer.pluralize
72
77
73
78
74 def set_user_lang(event):
79 def set_user_lang(event):
75 request = event.request
80 request = event.request
76 cur_user = getattr(request, 'user', None)
81 cur_user = getattr(request, 'user', None)
77
82
78 if cur_user:
83 if cur_user:
79 user_lang = cur_user.get_instance().user_data.get('language')
84 user_lang = cur_user.get_instance().user_data.get('language')
80 if user_lang:
85 if user_lang:
81 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
86 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
82 event.request._LOCALE_ = user_lang
87 event.request._LOCALE_ = user_lang
83
88
84
89
85 def add_pylons_context(event):
90 def add_pylons_context(event):
86 request = event.request
91 request = event.request
87
92
88 config = rhodecode.CONFIG
93 config = rhodecode.CONFIG
89 environ = request.environ
94 environ = request.environ
90 session = request.session
95 session = request.session
91
96
92 if hasattr(request, 'vcs_call'):
97 if hasattr(request, 'vcs_call'):
93 # skip vcs calls
98 # skip vcs calls
94 return
99 return
95
100
96 # Setup pylons globals.
101 # Setup pylons globals.
97 pylons.config._push_object(config)
102 pylons.config._push_object(config)
98 pylons.request._push_object(request)
103 pylons.request._push_object(request)
99 pylons.session._push_object(session)
104 pylons.session._push_object(session)
100 pylons.translator._push_object(_get_translator(config.get('lang')))
105 pylons.translator._push_object(_get_translator(config.get('lang')))
101
106
102 pylons.url._push_object(URLGenerator(config['routes.map'], environ))
107 pylons.url._push_object(URLGenerator(config['routes.map'], environ))
103 session_key = (
108 session_key = (
104 config['pylons.environ_config'].get('session', 'beaker.session'))
109 config['pylons.environ_config'].get('session', 'beaker.session'))
105 environ[session_key] = session
110 environ[session_key] = session
106
111
107 if hasattr(request, 'rpc_method'):
112 if hasattr(request, 'rpc_method'):
108 # skip api calls
113 # skip api calls
109 return
114 return
110
115
111 # Get the rhodecode auth user object and make it available.
116 # Get the rhodecode auth user object and make it available.
112 auth_user = get_auth_user(environ)
117 auth_user = get_auth_user(environ)
113 request.user = auth_user
118 request.user = auth_user
114 environ['rc_auth_user'] = auth_user
119 environ['rc_auth_user'] = auth_user
115
120
116 # Setup the pylons context object ('c')
121 # Setup the pylons context object ('c')
117 context = ContextObj()
122 context = ContextObj()
118 context.rhodecode_user = auth_user
123 context.rhodecode_user = auth_user
119 attach_context_attributes(context, request)
124 attach_context_attributes(context, request)
120 pylons.tmpl_context._push_object(context)
125 pylons.tmpl_context._push_object(context)
121
126
122
127
123 def scan_repositories_if_enabled(event):
128 def scan_repositories_if_enabled(event):
124 """
129 """
125 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
130 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
126 does a repository scan if enabled in the settings.
131 does a repository scan if enabled in the settings.
127 """
132 """
128 from rhodecode.model.scm import ScmModel
133 from rhodecode.model.scm import ScmModel
129 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
134 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
130 settings = event.app.registry.settings
135 settings = event.app.registry.settings
131 vcs_server_enabled = settings['vcs.server.enable']
136 vcs_server_enabled = settings['vcs.server.enable']
132 import_on_startup = settings['startup.import_repos']
137 import_on_startup = settings['startup.import_repos']
133 if vcs_server_enabled and import_on_startup:
138 if vcs_server_enabled and import_on_startup:
134 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
139 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
135 repo2db_mapper(repositories, remove_obsolete=False)
140 repo2db_mapper(repositories, remove_obsolete=False)
136
141
137
142
138 def write_metadata_if_needed(event):
143 def write_metadata_if_needed(event):
139 """
144 """
140 Writes upgrade metadata
145 Writes upgrade metadata
141 """
146 """
142 import rhodecode
147 import rhodecode
143 from rhodecode.lib import system_info
148 from rhodecode.lib import system_info
144 from rhodecode.lib import ext_json
149 from rhodecode.lib import ext_json
145
150
146 def write():
151 def write():
147 fname = '.rcmetadata.json'
152 fname = '.rcmetadata.json'
148 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
153 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
149 metadata_destination = os.path.join(ini_loc, fname)
154 metadata_destination = os.path.join(ini_loc, fname)
150
155
151 configuration = system_info.SysInfo(
156 configuration = system_info.SysInfo(
152 system_info.rhodecode_config)()['value']
157 system_info.rhodecode_config)()['value']
153 license_token = configuration['config']['license_token']
158 license_token = configuration['config']['license_token']
154 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
159 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
155 del dbinfo['url']
160 del dbinfo['url']
156 metadata = dict(
161 metadata = dict(
157 desc='upgrade metadata info',
162 desc='upgrade metadata info',
158 license_token=license_token,
163 license_token=license_token,
159 created_on=datetime.datetime.utcnow().isoformat(),
164 created_on=datetime.datetime.utcnow().isoformat(),
160 usage=system_info.SysInfo(system_info.usage_info)()['value'],
165 usage=system_info.SysInfo(system_info.usage_info)()['value'],
161 platform=system_info.SysInfo(system_info.platform_type)()['value'],
166 platform=system_info.SysInfo(system_info.platform_type)()['value'],
162 database=dbinfo,
167 database=dbinfo,
163 cpu=system_info.SysInfo(system_info.cpu)()['value'],
168 cpu=system_info.SysInfo(system_info.cpu)()['value'],
164 memory=system_info.SysInfo(system_info.memory)()['value'],
169 memory=system_info.SysInfo(system_info.memory)()['value'],
165 )
170 )
166
171
167 with open(metadata_destination, 'wb') as f:
172 with open(metadata_destination, 'wb') as f:
168 f.write(ext_json.json.dumps(metadata))
173 f.write(ext_json.json.dumps(metadata))
169
174
170 try:
175 try:
171 write()
176 write()
172 except Exception:
177 except Exception:
173 pass
178 pass
174
179
175
180
181 def write_js_routes_if_enabled(event):
182 registry = event.app.registry
183
184 mapper = registry.queryUtility(IRoutesMapper)
185 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
186
187 def _extract_route_information(route):
188 """
189 Convert a route into tuple(name, path, args), eg:
190 ('show_user', '/profile/%(username)s', ['username'])
191 """
192
193 routepath = route.pattern
194 pattern = route.pattern
195
196 def replace(matchobj):
197 if matchobj.group(1):
198 return "%%(%s)s" % matchobj.group(1).split(':')[0]
199 else:
200 return "%%(%s)s" % matchobj.group(2)
201
202 routepath = _argument_prog.sub(replace, routepath)
203
204 return (
205 route.name,
206 routepath,
207 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
208 for arg in _argument_prog.findall(pattern)]
209 )
210
211 def get_routes():
212 # pylons routes
213 for route in rhodecode.CONFIG['routes.map'].jsroutes():
214 yield route
215
216 # pyramid routes
217 for route in mapper.get_routes():
218 if not route.name.startswith('__'):
219 yield _extract_route_information(route)
220
221 if asbool(registry.settings.get('generate_js_files', 'false')):
222 static_path = AssetResolver().resolve('rhodecode:public').abspath()
223 jsroutes = get_routes()
224 jsroutes_file_content = generate_jsroutes_content(jsroutes)
225 jsroutes_file_path = os.path.join(
226 static_path, 'js', 'rhodecode', 'routes.js')
227
228 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
229 f.write(jsroutes_file_content)
230
231
176 class Subscriber(object):
232 class Subscriber(object):
177 """
233 """
178 Base class for subscribers to the pyramid event system.
234 Base class for subscribers to the pyramid event system.
179 """
235 """
180 def __call__(self, event):
236 def __call__(self, event):
181 self.run(event)
237 self.run(event)
182
238
183 def run(self, event):
239 def run(self, event):
184 raise NotImplementedError('Subclass has to implement this.')
240 raise NotImplementedError('Subclass has to implement this.')
185
241
186
242
187 class AsyncSubscriber(Subscriber):
243 class AsyncSubscriber(Subscriber):
188 """
244 """
189 Subscriber that handles the execution of events in a separate task to not
245 Subscriber that handles the execution of events in a separate task to not
190 block the execution of the code which triggers the event. It puts the
246 block the execution of the code which triggers the event. It puts the
191 received events into a queue from which the worker process takes them in
247 received events into a queue from which the worker process takes them in
192 order.
248 order.
193 """
249 """
194 def __init__(self):
250 def __init__(self):
195 self._stop = False
251 self._stop = False
196 self._eventq = Queue.Queue()
252 self._eventq = Queue.Queue()
197 self._worker = self.create_worker()
253 self._worker = self.create_worker()
198 self._worker.start()
254 self._worker.start()
199
255
200 def __call__(self, event):
256 def __call__(self, event):
201 self._eventq.put(event)
257 self._eventq.put(event)
202
258
203 def create_worker(self):
259 def create_worker(self):
204 worker = Thread(target=self.do_work)
260 worker = Thread(target=self.do_work)
205 worker.daemon = True
261 worker.daemon = True
206 return worker
262 return worker
207
263
208 def stop_worker(self):
264 def stop_worker(self):
209 self._stop = False
265 self._stop = False
210 self._eventq.put(None)
266 self._eventq.put(None)
211 self._worker.join()
267 self._worker.join()
212
268
213 def do_work(self):
269 def do_work(self):
214 while not self._stop:
270 while not self._stop:
215 event = self._eventq.get()
271 event = self._eventq.get()
216 if event is not None:
272 if event is not None:
217 self.run(event)
273 self.run(event)
218
274
219
275
220 class AsyncSubprocessSubscriber(AsyncSubscriber):
276 class AsyncSubprocessSubscriber(AsyncSubscriber):
221 """
277 """
222 Subscriber that uses the subprocess32 module to execute a command if an
278 Subscriber that uses the subprocess32 module to execute a command if an
223 event is received. Events are handled asynchronously.
279 event is received. Events are handled asynchronously.
224 """
280 """
225
281
226 def __init__(self, cmd, timeout=None):
282 def __init__(self, cmd, timeout=None):
227 super(AsyncSubprocessSubscriber, self).__init__()
283 super(AsyncSubprocessSubscriber, self).__init__()
228 self._cmd = cmd
284 self._cmd = cmd
229 self._timeout = timeout
285 self._timeout = timeout
230
286
231 def run(self, event):
287 def run(self, event):
232 cmd = self._cmd
288 cmd = self._cmd
233 timeout = self._timeout
289 timeout = self._timeout
234 log.debug('Executing command %s.', cmd)
290 log.debug('Executing command %s.', cmd)
235
291
236 try:
292 try:
237 output = subprocess32.check_output(
293 output = subprocess32.check_output(
238 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
294 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
239 log.debug('Command finished %s', cmd)
295 log.debug('Command finished %s', cmd)
240 if output:
296 if output:
241 log.debug('Command output: %s', output)
297 log.debug('Command output: %s', output)
242 except subprocess32.TimeoutExpired as e:
298 except subprocess32.TimeoutExpired as e:
243 log.exception('Timeout while executing command.')
299 log.exception('Timeout while executing command.')
244 if e.output:
300 if e.output:
245 log.error('Command output: %s', e.output)
301 log.error('Command output: %s', e.output)
246 except subprocess32.CalledProcessError as e:
302 except subprocess32.CalledProcessError as e:
247 log.exception('Error while executing command.')
303 log.exception('Error while executing command.')
248 if e.output:
304 if e.output:
249 log.error('Command output: %s', e.output)
305 log.error('Command output: %s', e.output)
250 except:
306 except:
251 log.exception(
307 log.exception(
252 'Exception while executing command %s.', cmd)
308 'Exception while executing command %s.', cmd)
General Comments 0
You need to be logged in to leave comments. Login now