##// END OF EJS Templates
mercurial: refactor how we fetch default largefile store....
marcink -
r1562:19b546f8 default
parent child Browse files
Show More
@@ -1,600 +1,601 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 Database creation, and setup module for RhodeCode Enterprise. Used for creation
22 Database creation, and setup module for RhodeCode Enterprise. Used for creation
23 of database as well as for migration operations
23 of database as well as for migration operations
24 """
24 """
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import time
28 import time
29 import uuid
29 import uuid
30 import logging
30 import logging
31 import getpass
31 import getpass
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from sqlalchemy.engine import create_engine
34 from sqlalchemy.engine import create_engine
35
35
36 from rhodecode import __dbversion__
36 from rhodecode import __dbversion__
37 from rhodecode.model import init_model
37 from rhodecode.model import init_model
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.db import (
39 from rhodecode.model.db import (
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
42 from rhodecode.model.meta import Session, Base
42 from rhodecode.model.meta import Session, Base
43 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.repo_group import RepoGroupModel
46 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 def notify(msg):
52 def notify(msg):
53 """
53 """
54 Notification for migrations messages
54 Notification for migrations messages
55 """
55 """
56 ml = len(msg) + (4 * 2)
56 ml = len(msg) + (4 * 2)
57 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
57 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
58
58
59
59
60 class DbManage(object):
60 class DbManage(object):
61
61
62 def __init__(self, log_sql, dbconf, root, tests=False,
62 def __init__(self, log_sql, dbconf, root, tests=False,
63 SESSION=None, cli_args={}):
63 SESSION=None, cli_args={}):
64 self.dbname = dbconf.split('/')[-1]
64 self.dbname = dbconf.split('/')[-1]
65 self.tests = tests
65 self.tests = tests
66 self.root = root
66 self.root = root
67 self.dburi = dbconf
67 self.dburi = dbconf
68 self.log_sql = log_sql
68 self.log_sql = log_sql
69 self.db_exists = False
69 self.db_exists = False
70 self.cli_args = cli_args
70 self.cli_args = cli_args
71 self.init_db(SESSION=SESSION)
71 self.init_db(SESSION=SESSION)
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
73
73
74 def get_ask_ok_func(self, param):
74 def get_ask_ok_func(self, param):
75 if param not in [None]:
75 if param not in [None]:
76 # return a function lambda that has a default set to param
76 # return a function lambda that has a default set to param
77 return lambda *args, **kwargs: param
77 return lambda *args, **kwargs: param
78 else:
78 else:
79 from rhodecode.lib.utils import ask_ok
79 from rhodecode.lib.utils import ask_ok
80 return ask_ok
80 return ask_ok
81
81
82 def init_db(self, SESSION=None):
82 def init_db(self, SESSION=None):
83 if SESSION:
83 if SESSION:
84 self.sa = SESSION
84 self.sa = SESSION
85 else:
85 else:
86 # init new sessions
86 # init new sessions
87 engine = create_engine(self.dburi, echo=self.log_sql)
87 engine = create_engine(self.dburi, echo=self.log_sql)
88 init_model(engine)
88 init_model(engine)
89 self.sa = Session()
89 self.sa = Session()
90
90
91 def create_tables(self, override=False):
91 def create_tables(self, override=False):
92 """
92 """
93 Create a auth database
93 Create a auth database
94 """
94 """
95
95
96 log.info("Existing database with the same name is going to be destroyed.")
96 log.info("Existing database with the same name is going to be destroyed.")
97 log.info("Setup command will run DROP ALL command on that database.")
97 log.info("Setup command will run DROP ALL command on that database.")
98 if self.tests:
98 if self.tests:
99 destroy = True
99 destroy = True
100 else:
100 else:
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
102 if not destroy:
102 if not destroy:
103 log.info('Nothing done.')
103 log.info('Nothing done.')
104 sys.exit(0)
104 sys.exit(0)
105 if destroy:
105 if destroy:
106 Base.metadata.drop_all()
106 Base.metadata.drop_all()
107
107
108 checkfirst = not override
108 checkfirst = not override
109 Base.metadata.create_all(checkfirst=checkfirst)
109 Base.metadata.create_all(checkfirst=checkfirst)
110 log.info('Created tables for %s' % self.dbname)
110 log.info('Created tables for %s' % self.dbname)
111
111
112 def set_db_version(self):
112 def set_db_version(self):
113 ver = DbMigrateVersion()
113 ver = DbMigrateVersion()
114 ver.version = __dbversion__
114 ver.version = __dbversion__
115 ver.repository_id = 'rhodecode_db_migrations'
115 ver.repository_id = 'rhodecode_db_migrations'
116 ver.repository_path = 'versions'
116 ver.repository_path = 'versions'
117 self.sa.add(ver)
117 self.sa.add(ver)
118 log.info('db version set to: %s' % __dbversion__)
118 log.info('db version set to: %s' % __dbversion__)
119
119
120 def run_pre_migration_tasks(self):
120 def run_pre_migration_tasks(self):
121 """
121 """
122 Run various tasks before actually doing migrations
122 Run various tasks before actually doing migrations
123 """
123 """
124 # delete cache keys on each upgrade
124 # delete cache keys on each upgrade
125 total = CacheKey.query().count()
125 total = CacheKey.query().count()
126 log.info("Deleting (%s) cache keys now...", total)
126 log.info("Deleting (%s) cache keys now...", total)
127 CacheKey.delete_all_cache()
127 CacheKey.delete_all_cache()
128
128
129 def upgrade(self):
129 def upgrade(self):
130 """
130 """
131 Upgrades given database schema to given revision following
131 Upgrades given database schema to given revision following
132 all needed steps, to perform the upgrade
132 all needed steps, to perform the upgrade
133
133
134 """
134 """
135
135
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
138 DatabaseNotControlledError
138 DatabaseNotControlledError
139
139
140 if 'sqlite' in self.dburi:
140 if 'sqlite' in self.dburi:
141 print (
141 print (
142 '********************** WARNING **********************\n'
142 '********************** WARNING **********************\n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
144 'Earlier versions are known to fail on some migrations\n'
144 'Earlier versions are known to fail on some migrations\n'
145 '*****************************************************\n')
145 '*****************************************************\n')
146
146
147 upgrade = self.ask_ok(
147 upgrade = self.ask_ok(
148 'You are about to perform a database upgrade. Make '
148 'You are about to perform a database upgrade. Make '
149 'sure you have backed up your database. '
149 'sure you have backed up your database. '
150 'Continue ? [y/n]')
150 'Continue ? [y/n]')
151 if not upgrade:
151 if not upgrade:
152 log.info('No upgrade performed')
152 log.info('No upgrade performed')
153 sys.exit(0)
153 sys.exit(0)
154
154
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
156 'rhodecode/lib/dbmigrate')
156 'rhodecode/lib/dbmigrate')
157 db_uri = self.dburi
157 db_uri = self.dburi
158
158
159 try:
159 try:
160 curr_version = api.db_version(db_uri, repository_path)
160 curr_version = api.db_version(db_uri, repository_path)
161 msg = ('Found current database under version '
161 msg = ('Found current database under version '
162 'control with version %s' % curr_version)
162 'control with version %s' % curr_version)
163
163
164 except (RuntimeError, DatabaseNotControlledError):
164 except (RuntimeError, DatabaseNotControlledError):
165 curr_version = 1
165 curr_version = 1
166 msg = ('Current database is not under version control. Setting '
166 msg = ('Current database is not under version control. Setting '
167 'as version %s' % curr_version)
167 'as version %s' % curr_version)
168 api.version_control(db_uri, repository_path, curr_version)
168 api.version_control(db_uri, repository_path, curr_version)
169
169
170 notify(msg)
170 notify(msg)
171
171
172 self.run_pre_migration_tasks()
172 self.run_pre_migration_tasks()
173
173
174 if curr_version == __dbversion__:
174 if curr_version == __dbversion__:
175 log.info('This database is already at the newest version')
175 log.info('This database is already at the newest version')
176 sys.exit(0)
176 sys.exit(0)
177
177
178 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
178 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
179 notify('attempting to upgrade database from '
179 notify('attempting to upgrade database from '
180 'version %s to version %s' % (curr_version, __dbversion__))
180 'version %s to version %s' % (curr_version, __dbversion__))
181
181
182 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
182 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
183 _step = None
183 _step = None
184 for step in upgrade_steps:
184 for step in upgrade_steps:
185 notify('performing upgrade step %s' % step)
185 notify('performing upgrade step %s' % step)
186 time.sleep(0.5)
186 time.sleep(0.5)
187
187
188 api.upgrade(db_uri, repository_path, step)
188 api.upgrade(db_uri, repository_path, step)
189 self.sa.rollback()
189 self.sa.rollback()
190 notify('schema upgrade for step %s completed' % (step,))
190 notify('schema upgrade for step %s completed' % (step,))
191
191
192 _step = step
192 _step = step
193
193
194 notify('upgrade to version %s successful' % _step)
194 notify('upgrade to version %s successful' % _step)
195
195
196 def fix_repo_paths(self):
196 def fix_repo_paths(self):
197 """
197 """
198 Fixes an old RhodeCode version path into new one without a '*'
198 Fixes an old RhodeCode version path into new one without a '*'
199 """
199 """
200
200
201 paths = self.sa.query(RhodeCodeUi)\
201 paths = self.sa.query(RhodeCodeUi)\
202 .filter(RhodeCodeUi.ui_key == '/')\
202 .filter(RhodeCodeUi.ui_key == '/')\
203 .scalar()
203 .scalar()
204
204
205 paths.ui_value = paths.ui_value.replace('*', '')
205 paths.ui_value = paths.ui_value.replace('*', '')
206
206
207 try:
207 try:
208 self.sa.add(paths)
208 self.sa.add(paths)
209 self.sa.commit()
209 self.sa.commit()
210 except Exception:
210 except Exception:
211 self.sa.rollback()
211 self.sa.rollback()
212 raise
212 raise
213
213
214 def fix_default_user(self):
214 def fix_default_user(self):
215 """
215 """
216 Fixes an old default user with some 'nicer' default values,
216 Fixes an old default user with some 'nicer' default values,
217 used mostly for anonymous access
217 used mostly for anonymous access
218 """
218 """
219 def_user = self.sa.query(User)\
219 def_user = self.sa.query(User)\
220 .filter(User.username == User.DEFAULT_USER)\
220 .filter(User.username == User.DEFAULT_USER)\
221 .one()
221 .one()
222
222
223 def_user.name = 'Anonymous'
223 def_user.name = 'Anonymous'
224 def_user.lastname = 'User'
224 def_user.lastname = 'User'
225 def_user.email = User.DEFAULT_USER_EMAIL
225 def_user.email = User.DEFAULT_USER_EMAIL
226
226
227 try:
227 try:
228 self.sa.add(def_user)
228 self.sa.add(def_user)
229 self.sa.commit()
229 self.sa.commit()
230 except Exception:
230 except Exception:
231 self.sa.rollback()
231 self.sa.rollback()
232 raise
232 raise
233
233
234 def fix_settings(self):
234 def fix_settings(self):
235 """
235 """
236 Fixes rhodecode settings and adds ga_code key for google analytics
236 Fixes rhodecode settings and adds ga_code key for google analytics
237 """
237 """
238
238
239 hgsettings3 = RhodeCodeSetting('ga_code', '')
239 hgsettings3 = RhodeCodeSetting('ga_code', '')
240
240
241 try:
241 try:
242 self.sa.add(hgsettings3)
242 self.sa.add(hgsettings3)
243 self.sa.commit()
243 self.sa.commit()
244 except Exception:
244 except Exception:
245 self.sa.rollback()
245 self.sa.rollback()
246 raise
246 raise
247
247
248 def create_admin_and_prompt(self):
248 def create_admin_and_prompt(self):
249
249
250 # defaults
250 # defaults
251 defaults = self.cli_args
251 defaults = self.cli_args
252 username = defaults.get('username')
252 username = defaults.get('username')
253 password = defaults.get('password')
253 password = defaults.get('password')
254 email = defaults.get('email')
254 email = defaults.get('email')
255
255
256 if username is None:
256 if username is None:
257 username = raw_input('Specify admin username:')
257 username = raw_input('Specify admin username:')
258 if password is None:
258 if password is None:
259 password = self._get_admin_password()
259 password = self._get_admin_password()
260 if not password:
260 if not password:
261 # second try
261 # second try
262 password = self._get_admin_password()
262 password = self._get_admin_password()
263 if not password:
263 if not password:
264 sys.exit()
264 sys.exit()
265 if email is None:
265 if email is None:
266 email = raw_input('Specify admin email:')
266 email = raw_input('Specify admin email:')
267 api_key = self.cli_args.get('api_key')
267 api_key = self.cli_args.get('api_key')
268 self.create_user(username, password, email, True,
268 self.create_user(username, password, email, True,
269 strict_creation_check=False,
269 strict_creation_check=False,
270 api_key=api_key)
270 api_key=api_key)
271
271
272 def _get_admin_password(self):
272 def _get_admin_password(self):
273 password = getpass.getpass('Specify admin password '
273 password = getpass.getpass('Specify admin password '
274 '(min 6 chars):')
274 '(min 6 chars):')
275 confirm = getpass.getpass('Confirm password:')
275 confirm = getpass.getpass('Confirm password:')
276
276
277 if password != confirm:
277 if password != confirm:
278 log.error('passwords mismatch')
278 log.error('passwords mismatch')
279 return False
279 return False
280 if len(password) < 6:
280 if len(password) < 6:
281 log.error('password is too short - use at least 6 characters')
281 log.error('password is too short - use at least 6 characters')
282 return False
282 return False
283
283
284 return password
284 return password
285
285
286 def create_test_admin_and_users(self):
286 def create_test_admin_and_users(self):
287 log.info('creating admin and regular test users')
287 log.info('creating admin and regular test users')
288 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
288 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
289 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
289 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
290 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
290 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
291 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
291 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
292 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
292 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
293
293
294 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
294 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
295 TEST_USER_ADMIN_EMAIL, True, api_key=True)
295 TEST_USER_ADMIN_EMAIL, True, api_key=True)
296
296
297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
298 TEST_USER_REGULAR_EMAIL, False, api_key=True)
298 TEST_USER_REGULAR_EMAIL, False, api_key=True)
299
299
300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
301 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
301 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
302
302
303 def create_ui_settings(self, repo_store_path):
303 def create_ui_settings(self, repo_store_path):
304 """
304 """
305 Creates ui settings, fills out hooks
305 Creates ui settings, fills out hooks
306 and disables dotencode
306 and disables dotencode
307 """
307 """
308 settings_model = SettingsModel(sa=self.sa)
308 settings_model = SettingsModel(sa=self.sa)
309 from rhodecode.lib.vcs.backends.hg import largefiles_store
309
310
310 # Build HOOKS
311 # Build HOOKS
311 hooks = [
312 hooks = [
312 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
313 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
313
314
314 # HG
315 # HG
315 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
316 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
316 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
317 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
317 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
318 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
318 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
319 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
319 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
320 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
320
321
321 ]
322 ]
322
323
323 for key, value in hooks:
324 for key, value in hooks:
324 hook_obj = settings_model.get_ui_by_key(key)
325 hook_obj = settings_model.get_ui_by_key(key)
325 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
326 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
326 hooks2.ui_section = 'hooks'
327 hooks2.ui_section = 'hooks'
327 hooks2.ui_key = key
328 hooks2.ui_key = key
328 hooks2.ui_value = value
329 hooks2.ui_value = value
329 self.sa.add(hooks2)
330 self.sa.add(hooks2)
330
331
331 # enable largefiles
332 # enable largefiles
332 largefiles = RhodeCodeUi()
333 largefiles = RhodeCodeUi()
333 largefiles.ui_section = 'extensions'
334 largefiles.ui_section = 'extensions'
334 largefiles.ui_key = 'largefiles'
335 largefiles.ui_key = 'largefiles'
335 largefiles.ui_value = ''
336 largefiles.ui_value = ''
336 self.sa.add(largefiles)
337 self.sa.add(largefiles)
337
338
338 # set default largefiles cache dir, defaults to
339 # set default largefiles cache dir, defaults to
339 # /repo location/.cache/largefiles
340 # /repo_store_location/.cache/largefiles
340 largefiles = RhodeCodeUi()
341 largefiles = RhodeCodeUi()
341 largefiles.ui_section = 'largefiles'
342 largefiles.ui_section = 'largefiles'
342 largefiles.ui_key = 'usercache'
343 largefiles.ui_key = 'usercache'
343 largefiles.ui_value = os.path.join(repo_store_path, '.cache',
344 largefiles.ui_value = largefiles_store(repo_store_path)
344 'largefiles')
345
345 self.sa.add(largefiles)
346 self.sa.add(largefiles)
346
347
347 # enable hgsubversion disabled by default
348 # enable hgsubversion disabled by default
348 hgsubversion = RhodeCodeUi()
349 hgsubversion = RhodeCodeUi()
349 hgsubversion.ui_section = 'extensions'
350 hgsubversion.ui_section = 'extensions'
350 hgsubversion.ui_key = 'hgsubversion'
351 hgsubversion.ui_key = 'hgsubversion'
351 hgsubversion.ui_value = ''
352 hgsubversion.ui_value = ''
352 hgsubversion.ui_active = False
353 hgsubversion.ui_active = False
353 self.sa.add(hgsubversion)
354 self.sa.add(hgsubversion)
354
355
355 # enable hggit disabled by default
356 # enable hggit disabled by default
356 hggit = RhodeCodeUi()
357 hggit = RhodeCodeUi()
357 hggit.ui_section = 'extensions'
358 hggit.ui_section = 'extensions'
358 hggit.ui_key = 'hggit'
359 hggit.ui_key = 'hggit'
359 hggit.ui_value = ''
360 hggit.ui_value = ''
360 hggit.ui_active = False
361 hggit.ui_active = False
361 self.sa.add(hggit)
362 self.sa.add(hggit)
362
363
363 # set svn branch defaults
364 # set svn branch defaults
364 branches = ["/branches/*", "/trunk"]
365 branches = ["/branches/*", "/trunk"]
365 tags = ["/tags/*"]
366 tags = ["/tags/*"]
366
367
367 for branch in branches:
368 for branch in branches:
368 settings_model.create_ui_section_value(
369 settings_model.create_ui_section_value(
369 RhodeCodeUi.SVN_BRANCH_ID, branch)
370 RhodeCodeUi.SVN_BRANCH_ID, branch)
370
371
371 for tag in tags:
372 for tag in tags:
372 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
373 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
373
374
374 def create_auth_plugin_options(self, skip_existing=False):
375 def create_auth_plugin_options(self, skip_existing=False):
375 """
376 """
376 Create default auth plugin settings, and make it active
377 Create default auth plugin settings, and make it active
377
378
378 :param skip_existing:
379 :param skip_existing:
379 """
380 """
380
381
381 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
382 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
382 ('auth_rhodecode_enabled', 'True', 'bool')]:
383 ('auth_rhodecode_enabled', 'True', 'bool')]:
383 if (skip_existing and
384 if (skip_existing and
384 SettingsModel().get_setting_by_name(k) is not None):
385 SettingsModel().get_setting_by_name(k) is not None):
385 log.debug('Skipping option %s' % k)
386 log.debug('Skipping option %s' % k)
386 continue
387 continue
387 setting = RhodeCodeSetting(k, v, t)
388 setting = RhodeCodeSetting(k, v, t)
388 self.sa.add(setting)
389 self.sa.add(setting)
389
390
390 def create_default_options(self, skip_existing=False):
391 def create_default_options(self, skip_existing=False):
391 """Creates default settings"""
392 """Creates default settings"""
392
393
393 for k, v, t in [
394 for k, v, t in [
394 ('default_repo_enable_locking', False, 'bool'),
395 ('default_repo_enable_locking', False, 'bool'),
395 ('default_repo_enable_downloads', False, 'bool'),
396 ('default_repo_enable_downloads', False, 'bool'),
396 ('default_repo_enable_statistics', False, 'bool'),
397 ('default_repo_enable_statistics', False, 'bool'),
397 ('default_repo_private', False, 'bool'),
398 ('default_repo_private', False, 'bool'),
398 ('default_repo_type', 'hg', 'unicode')]:
399 ('default_repo_type', 'hg', 'unicode')]:
399
400
400 if (skip_existing and
401 if (skip_existing and
401 SettingsModel().get_setting_by_name(k) is not None):
402 SettingsModel().get_setting_by_name(k) is not None):
402 log.debug('Skipping option %s' % k)
403 log.debug('Skipping option %s' % k)
403 continue
404 continue
404 setting = RhodeCodeSetting(k, v, t)
405 setting = RhodeCodeSetting(k, v, t)
405 self.sa.add(setting)
406 self.sa.add(setting)
406
407
407 def fixup_groups(self):
408 def fixup_groups(self):
408 def_usr = User.get_default_user()
409 def_usr = User.get_default_user()
409 for g in RepoGroup.query().all():
410 for g in RepoGroup.query().all():
410 g.group_name = g.get_new_name(g.name)
411 g.group_name = g.get_new_name(g.name)
411 self.sa.add(g)
412 self.sa.add(g)
412 # get default perm
413 # get default perm
413 default = UserRepoGroupToPerm.query()\
414 default = UserRepoGroupToPerm.query()\
414 .filter(UserRepoGroupToPerm.group == g)\
415 .filter(UserRepoGroupToPerm.group == g)\
415 .filter(UserRepoGroupToPerm.user == def_usr)\
416 .filter(UserRepoGroupToPerm.user == def_usr)\
416 .scalar()
417 .scalar()
417
418
418 if default is None:
419 if default is None:
419 log.debug('missing default permission for group %s adding' % g)
420 log.debug('missing default permission for group %s adding' % g)
420 perm_obj = RepoGroupModel()._create_default_perms(g)
421 perm_obj = RepoGroupModel()._create_default_perms(g)
421 self.sa.add(perm_obj)
422 self.sa.add(perm_obj)
422
423
423 def reset_permissions(self, username):
424 def reset_permissions(self, username):
424 """
425 """
425 Resets permissions to default state, useful when old systems had
426 Resets permissions to default state, useful when old systems had
426 bad permissions, we must clean them up
427 bad permissions, we must clean them up
427
428
428 :param username:
429 :param username:
429 """
430 """
430 default_user = User.get_by_username(username)
431 default_user = User.get_by_username(username)
431 if not default_user:
432 if not default_user:
432 return
433 return
433
434
434 u2p = UserToPerm.query()\
435 u2p = UserToPerm.query()\
435 .filter(UserToPerm.user == default_user).all()
436 .filter(UserToPerm.user == default_user).all()
436 fixed = False
437 fixed = False
437 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
438 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
438 for p in u2p:
439 for p in u2p:
439 Session().delete(p)
440 Session().delete(p)
440 fixed = True
441 fixed = True
441 self.populate_default_permissions()
442 self.populate_default_permissions()
442 return fixed
443 return fixed
443
444
444 def update_repo_info(self):
445 def update_repo_info(self):
445 RepoModel.update_repoinfo()
446 RepoModel.update_repoinfo()
446
447
447 def config_prompt(self, test_repo_path='', retries=3):
448 def config_prompt(self, test_repo_path='', retries=3):
448 defaults = self.cli_args
449 defaults = self.cli_args
449 _path = defaults.get('repos_location')
450 _path = defaults.get('repos_location')
450 if retries == 3:
451 if retries == 3:
451 log.info('Setting up repositories config')
452 log.info('Setting up repositories config')
452
453
453 if _path is not None:
454 if _path is not None:
454 path = _path
455 path = _path
455 elif not self.tests and not test_repo_path:
456 elif not self.tests and not test_repo_path:
456 path = raw_input(
457 path = raw_input(
457 'Enter a valid absolute path to store repositories. '
458 'Enter a valid absolute path to store repositories. '
458 'All repositories in that path will be added automatically:'
459 'All repositories in that path will be added automatically:'
459 )
460 )
460 else:
461 else:
461 path = test_repo_path
462 path = test_repo_path
462 path_ok = True
463 path_ok = True
463
464
464 # check proper dir
465 # check proper dir
465 if not os.path.isdir(path):
466 if not os.path.isdir(path):
466 path_ok = False
467 path_ok = False
467 log.error('Given path %s is not a valid directory' % (path,))
468 log.error('Given path %s is not a valid directory' % (path,))
468
469
469 elif not os.path.isabs(path):
470 elif not os.path.isabs(path):
470 path_ok = False
471 path_ok = False
471 log.error('Given path %s is not an absolute path' % (path,))
472 log.error('Given path %s is not an absolute path' % (path,))
472
473
473 # check if path is at least readable.
474 # check if path is at least readable.
474 if not os.access(path, os.R_OK):
475 if not os.access(path, os.R_OK):
475 path_ok = False
476 path_ok = False
476 log.error('Given path %s is not readable' % (path,))
477 log.error('Given path %s is not readable' % (path,))
477
478
478 # check write access, warn user about non writeable paths
479 # check write access, warn user about non writeable paths
479 elif not os.access(path, os.W_OK) and path_ok:
480 elif not os.access(path, os.W_OK) and path_ok:
480 log.warning('No write permission to given path %s' % (path,))
481 log.warning('No write permission to given path %s' % (path,))
481
482
482 q = ('Given path %s is not writeable, do you want to '
483 q = ('Given path %s is not writeable, do you want to '
483 'continue with read only mode ? [y/n]' % (path,))
484 'continue with read only mode ? [y/n]' % (path,))
484 if not self.ask_ok(q):
485 if not self.ask_ok(q):
485 log.error('Canceled by user')
486 log.error('Canceled by user')
486 sys.exit(-1)
487 sys.exit(-1)
487
488
488 if retries == 0:
489 if retries == 0:
489 sys.exit('max retries reached')
490 sys.exit('max retries reached')
490 if not path_ok:
491 if not path_ok:
491 retries -= 1
492 retries -= 1
492 return self.config_prompt(test_repo_path, retries)
493 return self.config_prompt(test_repo_path, retries)
493
494
494 real_path = os.path.normpath(os.path.realpath(path))
495 real_path = os.path.normpath(os.path.realpath(path))
495
496
496 if real_path != os.path.normpath(path):
497 if real_path != os.path.normpath(path):
497 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
498 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
498 'given path as %s ? [y/n]') % (real_path,)
499 'given path as %s ? [y/n]') % (real_path,)
499 if not self.ask_ok(q):
500 if not self.ask_ok(q):
500 log.error('Canceled by user')
501 log.error('Canceled by user')
501 sys.exit(-1)
502 sys.exit(-1)
502
503
503 return real_path
504 return real_path
504
505
505 def create_settings(self, path):
506 def create_settings(self, path):
506
507
507 self.create_ui_settings(path)
508 self.create_ui_settings(path)
508
509
509 ui_config = [
510 ui_config = [
510 ('web', 'push_ssl', 'false'),
511 ('web', 'push_ssl', 'false'),
511 ('web', 'allow_archive', 'gz zip bz2'),
512 ('web', 'allow_archive', 'gz zip bz2'),
512 ('web', 'allow_push', '*'),
513 ('web', 'allow_push', '*'),
513 ('web', 'baseurl', '/'),
514 ('web', 'baseurl', '/'),
514 ('paths', '/', path),
515 ('paths', '/', path),
515 ('phases', 'publish', 'true')
516 ('phases', 'publish', 'true')
516 ]
517 ]
517 for section, key, value in ui_config:
518 for section, key, value in ui_config:
518 ui_conf = RhodeCodeUi()
519 ui_conf = RhodeCodeUi()
519 setattr(ui_conf, 'ui_section', section)
520 setattr(ui_conf, 'ui_section', section)
520 setattr(ui_conf, 'ui_key', key)
521 setattr(ui_conf, 'ui_key', key)
521 setattr(ui_conf, 'ui_value', value)
522 setattr(ui_conf, 'ui_value', value)
522 self.sa.add(ui_conf)
523 self.sa.add(ui_conf)
523
524
524 # rhodecode app settings
525 # rhodecode app settings
525 settings = [
526 settings = [
526 ('realm', 'RhodeCode', 'unicode'),
527 ('realm', 'RhodeCode', 'unicode'),
527 ('title', '', 'unicode'),
528 ('title', '', 'unicode'),
528 ('pre_code', '', 'unicode'),
529 ('pre_code', '', 'unicode'),
529 ('post_code', '', 'unicode'),
530 ('post_code', '', 'unicode'),
530 ('show_public_icon', True, 'bool'),
531 ('show_public_icon', True, 'bool'),
531 ('show_private_icon', True, 'bool'),
532 ('show_private_icon', True, 'bool'),
532 ('stylify_metatags', False, 'bool'),
533 ('stylify_metatags', False, 'bool'),
533 ('dashboard_items', 100, 'int'),
534 ('dashboard_items', 100, 'int'),
534 ('admin_grid_items', 25, 'int'),
535 ('admin_grid_items', 25, 'int'),
535 ('show_version', True, 'bool'),
536 ('show_version', True, 'bool'),
536 ('use_gravatar', False, 'bool'),
537 ('use_gravatar', False, 'bool'),
537 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
538 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
538 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
539 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
539 ('support_url', '', 'unicode'),
540 ('support_url', '', 'unicode'),
540 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
541 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
541 ('show_revision_number', True, 'bool'),
542 ('show_revision_number', True, 'bool'),
542 ('show_sha_length', 12, 'int'),
543 ('show_sha_length', 12, 'int'),
543 ]
544 ]
544
545
545 for key, val, type_ in settings:
546 for key, val, type_ in settings:
546 sett = RhodeCodeSetting(key, val, type_)
547 sett = RhodeCodeSetting(key, val, type_)
547 self.sa.add(sett)
548 self.sa.add(sett)
548
549
549 self.create_auth_plugin_options()
550 self.create_auth_plugin_options()
550 self.create_default_options()
551 self.create_default_options()
551
552
552 log.info('created ui config')
553 log.info('created ui config')
553
554
554 def create_user(self, username, password, email='', admin=False,
555 def create_user(self, username, password, email='', admin=False,
555 strict_creation_check=True, api_key=None):
556 strict_creation_check=True, api_key=None):
556 log.info('creating user %s' % username)
557 log.info('creating user %s' % username)
557 user = UserModel().create_or_update(
558 user = UserModel().create_or_update(
558 username, password, email, firstname='RhodeCode', lastname='Admin',
559 username, password, email, firstname='RhodeCode', lastname='Admin',
559 active=True, admin=admin, extern_type="rhodecode",
560 active=True, admin=admin, extern_type="rhodecode",
560 strict_creation_check=strict_creation_check)
561 strict_creation_check=strict_creation_check)
561
562
562 if api_key:
563 if api_key:
563 log.info('setting a provided api key for the user %s', username)
564 log.info('setting a provided api key for the user %s', username)
564 from rhodecode.model.auth_token import AuthTokenModel
565 from rhodecode.model.auth_token import AuthTokenModel
565 AuthTokenModel().create(
566 AuthTokenModel().create(
566 user=user, description='BUILTIN TOKEN')
567 user=user, description='BUILTIN TOKEN')
567
568
568 def create_default_user(self):
569 def create_default_user(self):
569 log.info('creating default user')
570 log.info('creating default user')
570 # create default user for handling default permissions.
571 # create default user for handling default permissions.
571 user = UserModel().create_or_update(username=User.DEFAULT_USER,
572 user = UserModel().create_or_update(username=User.DEFAULT_USER,
572 password=str(uuid.uuid1())[:20],
573 password=str(uuid.uuid1())[:20],
573 email=User.DEFAULT_USER_EMAIL,
574 email=User.DEFAULT_USER_EMAIL,
574 firstname='Anonymous',
575 firstname='Anonymous',
575 lastname='User',
576 lastname='User',
576 strict_creation_check=False)
577 strict_creation_check=False)
577 # based on configuration options activate/deactive this user which
578 # based on configuration options activate/deactive this user which
578 # controlls anonymous access
579 # controlls anonymous access
579 if self.cli_args.get('public_access') is False:
580 if self.cli_args.get('public_access') is False:
580 log.info('Public access disabled')
581 log.info('Public access disabled')
581 user.active = False
582 user.active = False
582 Session().add(user)
583 Session().add(user)
583 Session().commit()
584 Session().commit()
584
585
585 def create_permissions(self):
586 def create_permissions(self):
586 """
587 """
587 Creates all permissions defined in the system
588 Creates all permissions defined in the system
588 """
589 """
589 # module.(access|create|change|delete)_[name]
590 # module.(access|create|change|delete)_[name]
590 # module.(none|read|write|admin)
591 # module.(none|read|write|admin)
591 log.info('creating permissions')
592 log.info('creating permissions')
592 PermissionModel(self.sa).create_permissions()
593 PermissionModel(self.sa).create_permissions()
593
594
594 def populate_default_permissions(self):
595 def populate_default_permissions(self):
595 """
596 """
596 Populate default permissions. It will create only the default
597 Populate default permissions. It will create only the default
597 permissions that are missing, and not alter already defined ones
598 permissions that are missing, and not alter already defined ones
598 """
599 """
599 log.info('creating default user permissions')
600 log.info('creating default user permissions')
600 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
601 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
@@ -1,48 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2017 RhodeCode GmbH
3 # Copyright (C) 2014-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 HG module
22 HG module
23 """
23 """
24 import os
24 import logging
25 import logging
25
26
26 from rhodecode.lib.vcs import connection
27 from rhodecode.lib.vcs import connection
27 from rhodecode.lib.vcs.backends.hg.commit import MercurialCommit
28 from rhodecode.lib.vcs.backends.hg.commit import MercurialCommit
28 from rhodecode.lib.vcs.backends.hg.inmemory import MercurialInMemoryCommit
29 from rhodecode.lib.vcs.backends.hg.inmemory import MercurialInMemoryCommit
29 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
30 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
30
31
31
32
32 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
33
34
34
35
35 def discover_hg_version(raise_on_exc=False):
36 def discover_hg_version(raise_on_exc=False):
36 """
37 """
37 Returns the string as it was returned by running 'git --version'
38 Returns the string as it was returned by running 'git --version'
38
39
39 It will return an empty string in case the connection is not initialized
40 It will return an empty string in case the connection is not initialized
40 or no vcsserver is available.
41 or no vcsserver is available.
41 """
42 """
42 try:
43 try:
43 return connection.Hg.discover_hg_version()
44 return connection.Hg.discover_hg_version()
44 except Exception:
45 except Exception:
45 log.warning("Failed to discover the HG version", exc_info=True)
46 log.warning("Failed to discover the HG version", exc_info=True)
46 if raise_on_exc:
47 if raise_on_exc:
47 raise
48 raise
48 return ''
49 return ''
50
51
52 def largefiles_store(base_location):
53 """
54 Return a largefile store relative to base_location
55 """
56 return os.path.join(base_location, '.cache', 'largefiles')
@@ -1,275 +1,282 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 from subprocess32 import Popen, PIPE
21 from subprocess32 import Popen, PIPE
22 import os
22 import os
23 import shutil
23 import shutil
24 import sys
24 import sys
25 import tempfile
25 import tempfile
26
26
27 import pytest
27 import pytest
28 from sqlalchemy.engine import url
28 from sqlalchemy.engine import url
29
29
30 from rhodecode.tests.fixture import TestINI
30 from rhodecode.tests.fixture import TestINI
31
31
32
32
33 def _get_dbs_from_metafunc(metafunc):
33 def _get_dbs_from_metafunc(metafunc):
34 if hasattr(metafunc.function, 'dbs'):
34 if hasattr(metafunc.function, 'dbs'):
35 # Supported backends by this test function, created from
35 # Supported backends by this test function, created from
36 # pytest.mark.dbs
36 # pytest.mark.dbs
37 backends = metafunc.function.dbs.args
37 backends = metafunc.function.dbs.args
38 else:
38 else:
39 backends = metafunc.config.getoption('--dbs')
39 backends = metafunc.config.getoption('--dbs')
40 return backends
40 return backends
41
41
42
42
43 def pytest_generate_tests(metafunc):
43 def pytest_generate_tests(metafunc):
44 # Support test generation based on --dbs parameter
44 # Support test generation based on --dbs parameter
45 if 'db_backend' in metafunc.fixturenames:
45 if 'db_backend' in metafunc.fixturenames:
46 requested_backends = set(metafunc.config.getoption('--dbs'))
46 requested_backends = set(metafunc.config.getoption('--dbs'))
47 backends = _get_dbs_from_metafunc(metafunc)
47 backends = _get_dbs_from_metafunc(metafunc)
48 backends = requested_backends.intersection(backends)
48 backends = requested_backends.intersection(backends)
49 # TODO: johbo: Disabling a backend did not work out with
49 # TODO: johbo: Disabling a backend did not work out with
50 # parametrization, find better way to achieve this.
50 # parametrization, find better way to achieve this.
51 if not backends:
51 if not backends:
52 metafunc.function._skip = True
52 metafunc.function._skip = True
53 metafunc.parametrize('db_backend_name', backends)
53 metafunc.parametrize('db_backend_name', backends)
54
54
55
55
56 def pytest_collection_modifyitems(session, config, items):
56 def pytest_collection_modifyitems(session, config, items):
57 remaining = [
57 remaining = [
58 i for i in items if not getattr(i.obj, '_skip', False)]
58 i for i in items if not getattr(i.obj, '_skip', False)]
59 items[:] = remaining
59 items[:] = remaining
60
60
61
61
62 @pytest.fixture
62 @pytest.fixture
63 def db_backend(
63 def db_backend(
64 request, db_backend_name, pylons_config, tmpdir_factory):
64 request, db_backend_name, pylons_config, tmpdir_factory):
65 basetemp = tmpdir_factory.getbasetemp().strpath
65 basetemp = tmpdir_factory.getbasetemp().strpath
66 klass = _get_backend(db_backend_name)
66 klass = _get_backend(db_backend_name)
67
67
68 option_name = '--{}-connection-string'.format(db_backend_name)
68 option_name = '--{}-connection-string'.format(db_backend_name)
69 connection_string = request.config.getoption(option_name) or None
69 connection_string = request.config.getoption(option_name) or None
70
70
71 return klass(
71 return klass(
72 config_file=pylons_config, basetemp=basetemp,
72 config_file=pylons_config, basetemp=basetemp,
73 connection_string=connection_string)
73 connection_string=connection_string)
74
74
75
75
76 def _get_backend(backend_type):
76 def _get_backend(backend_type):
77 return {
77 return {
78 'sqlite': SQLiteDBBackend,
78 'sqlite': SQLiteDBBackend,
79 'postgres': PostgresDBBackend,
79 'postgres': PostgresDBBackend,
80 'mysql': MySQLDBBackend,
80 'mysql': MySQLDBBackend,
81 '': EmptyDBBackend
81 '': EmptyDBBackend
82 }[backend_type]
82 }[backend_type]
83
83
84
84
85 class DBBackend(object):
85 class DBBackend(object):
86 _store = os.path.dirname(os.path.abspath(__file__))
86 _store = os.path.dirname(os.path.abspath(__file__))
87 _type = None
87 _type = None
88 _base_ini_config = [{'app:main': {'vcs.start_server': 'false'}}]
88 _base_ini_config = [{'app:main': {'vcs.start_server': 'false'}}]
89 _db_url = [{'app:main': {'sqlalchemy.db1.url': ''}}]
89 _db_url = [{'app:main': {'sqlalchemy.db1.url': ''}}]
90 _base_db_name = 'rhodecode_test_db_backend'
90 _base_db_name = 'rhodecode_test_db_backend'
91
91
92 def __init__(
92 def __init__(
93 self, config_file, db_name=None, basetemp=None,
93 self, config_file, db_name=None, basetemp=None,
94 connection_string=None):
94 connection_string=None):
95
96 from rhodecode.lib.vcs.backends.hg import largefiles_store
97
95 self.fixture_store = os.path.join(self._store, self._type)
98 self.fixture_store = os.path.join(self._store, self._type)
96 self.db_name = db_name or self._base_db_name
99 self.db_name = db_name or self._base_db_name
97 self._base_ini_file = config_file
100 self._base_ini_file = config_file
98 self.stderr = ''
101 self.stderr = ''
99 self.stdout = ''
102 self.stdout = ''
100 self._basetemp = basetemp or tempfile.gettempdir()
103 self._basetemp = basetemp or tempfile.gettempdir()
101 self._repos_location = os.path.join(self._basetemp, 'rc_test_repos')
104 self._repos_location = os.path.join(self._basetemp, 'rc_test_repos')
105 self._repos_hg_largefiles_store = largefiles_store(self._basetemp)
102 self.connection_string = connection_string
106 self.connection_string = connection_string
103
107
104 @property
108 @property
105 def connection_string(self):
109 def connection_string(self):
106 return self._connection_string
110 return self._connection_string
107
111
108 @connection_string.setter
112 @connection_string.setter
109 def connection_string(self, new_connection_string):
113 def connection_string(self, new_connection_string):
110 if not new_connection_string:
114 if not new_connection_string:
111 new_connection_string = self.get_default_connection_string()
115 new_connection_string = self.get_default_connection_string()
112 else:
116 else:
113 new_connection_string = new_connection_string.format(
117 new_connection_string = new_connection_string.format(
114 db_name=self.db_name)
118 db_name=self.db_name)
115 url_parts = url.make_url(new_connection_string)
119 url_parts = url.make_url(new_connection_string)
116 self._connection_string = new_connection_string
120 self._connection_string = new_connection_string
117 self.user = url_parts.username
121 self.user = url_parts.username
118 self.password = url_parts.password
122 self.password = url_parts.password
119 self.host = url_parts.host
123 self.host = url_parts.host
120
124
121 def get_default_connection_string(self):
125 def get_default_connection_string(self):
122 raise NotImplementedError('default connection_string is required.')
126 raise NotImplementedError('default connection_string is required.')
123
127
124 def execute(self, cmd, env=None, *args):
128 def execute(self, cmd, env=None, *args):
125 """
129 """
126 Runs command on the system with given ``args``.
130 Runs command on the system with given ``args``.
127 """
131 """
128
132
129 command = cmd + ' ' + ' '.join(args)
133 command = cmd + ' ' + ' '.join(args)
130 sys.stdout.write(command)
134 sys.stdout.write(command)
131
135
132 # Tell Python to use UTF-8 encoding out stdout
136 # Tell Python to use UTF-8 encoding out stdout
133 _env = os.environ.copy()
137 _env = os.environ.copy()
134 _env['PYTHONIOENCODING'] = 'UTF-8'
138 _env['PYTHONIOENCODING'] = 'UTF-8'
135 if env:
139 if env:
136 _env.update(env)
140 _env.update(env)
137 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
141 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
138 self.stdout, self.stderr = self.p.communicate()
142 self.stdout, self.stderr = self.p.communicate()
139 sys.stdout.write('COMMAND:'+command+'\n')
143 sys.stdout.write('COMMAND:'+command+'\n')
140 sys.stdout.write(self.stdout)
144 sys.stdout.write(self.stdout)
141 return self.stdout, self.stderr
145 return self.stdout, self.stderr
142
146
143 def assert_returncode_success(self):
147 def assert_returncode_success(self):
144 if not self.p.returncode == 0:
148 if not self.p.returncode == 0:
145 print(self.stderr)
149 print(self.stderr)
146 raise AssertionError('non 0 retcode:{}'.format(self.p.returncode))
150 raise AssertionError('non 0 retcode:{}'.format(self.p.returncode))
147
151
148 def setup_rhodecode_db(self, ini_params=None, env=None):
152 def setup_rhodecode_db(self, ini_params=None, env=None):
149 if not ini_params:
153 if not ini_params:
150 ini_params = self._base_ini_config
154 ini_params = self._base_ini_config
151
155
152 ini_params.extend(self._db_url)
156 ini_params.extend(self._db_url)
153 with TestINI(self._base_ini_file, ini_params,
157 with TestINI(self._base_ini_file, ini_params,
154 self._type, destroy=True) as _ini_file:
158 self._type, destroy=True) as _ini_file:
155 if not os.path.isdir(self._repos_location):
159 if not os.path.isdir(self._repos_location):
156 os.makedirs(self._repos_location)
160 os.makedirs(self._repos_location)
161 if not os.path.isdir(self._repos_hg_largefiles_store):
162 os.makedirs(self._repos_hg_largefiles_store)
163
157 self.execute(
164 self.execute(
158 "paster setup-rhodecode {0} --user=marcink "
165 "paster setup-rhodecode {0} --user=marcink "
159 "--email=marcin@rhodeocode.com --password={1} "
166 "--email=marcin@rhodeocode.com --password={1} "
160 "--repos={2} --force-yes".format(
167 "--repos={2} --force-yes".format(
161 _ini_file, 'qweqwe', self._repos_location), env=env)
168 _ini_file, 'qweqwe', self._repos_location), env=env)
162
169
163 def upgrade_database(self, ini_params=None):
170 def upgrade_database(self, ini_params=None):
164 if not ini_params:
171 if not ini_params:
165 ini_params = self._base_ini_config
172 ini_params = self._base_ini_config
166 ini_params.extend(self._db_url)
173 ini_params.extend(self._db_url)
167
174
168 test_ini = TestINI(
175 test_ini = TestINI(
169 self._base_ini_file, ini_params, self._type, destroy=True)
176 self._base_ini_file, ini_params, self._type, destroy=True)
170 with test_ini as ini_file:
177 with test_ini as ini_file:
171 if not os.path.isdir(self._repos_location):
178 if not os.path.isdir(self._repos_location):
172 os.makedirs(self._repos_location)
179 os.makedirs(self._repos_location)
173 self.execute(
180 self.execute(
174 "paster upgrade-db {} --force-yes".format(ini_file))
181 "paster upgrade-db {} --force-yes".format(ini_file))
175
182
176 def setup_db(self):
183 def setup_db(self):
177 raise NotImplementedError
184 raise NotImplementedError
178
185
179 def teardown_db(self):
186 def teardown_db(self):
180 raise NotImplementedError
187 raise NotImplementedError
181
188
182 def import_dump(self, dumpname):
189 def import_dump(self, dumpname):
183 raise NotImplementedError
190 raise NotImplementedError
184
191
185
192
186 class EmptyDBBackend(DBBackend):
193 class EmptyDBBackend(DBBackend):
187 _type = ''
194 _type = ''
188
195
189 def setup_db(self):
196 def setup_db(self):
190 pass
197 pass
191
198
192 def teardown_db(self):
199 def teardown_db(self):
193 pass
200 pass
194
201
195 def import_dump(self, dumpname):
202 def import_dump(self, dumpname):
196 pass
203 pass
197
204
198 def assert_returncode_success(self):
205 def assert_returncode_success(self):
199 assert True
206 assert True
200
207
201
208
202 class SQLiteDBBackend(DBBackend):
209 class SQLiteDBBackend(DBBackend):
203 _type = 'sqlite'
210 _type = 'sqlite'
204
211
205 def get_default_connection_string(self):
212 def get_default_connection_string(self):
206 return 'sqlite:///{}/{}.sqlite'.format(self._basetemp, self.db_name)
213 return 'sqlite:///{}/{}.sqlite'.format(self._basetemp, self.db_name)
207
214
208 def setup_db(self):
215 def setup_db(self):
209 # dump schema for tests
216 # dump schema for tests
210 # cp -v $TEST_DB_NAME
217 # cp -v $TEST_DB_NAME
211 self._db_url = [{'app:main': {
218 self._db_url = [{'app:main': {
212 'sqlalchemy.db1.url': self.connection_string}}]
219 'sqlalchemy.db1.url': self.connection_string}}]
213
220
214 def import_dump(self, dumpname):
221 def import_dump(self, dumpname):
215 dump = os.path.join(self.fixture_store, dumpname)
222 dump = os.path.join(self.fixture_store, dumpname)
216 shutil.copy(
223 shutil.copy(
217 dump,
224 dump,
218 os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self)))
225 os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self)))
219
226
220 def teardown_db(self):
227 def teardown_db(self):
221 self.execute("rm -rf {}.sqlite".format(
228 self.execute("rm -rf {}.sqlite".format(
222 os.path.join(self._basetemp, self.db_name)))
229 os.path.join(self._basetemp, self.db_name)))
223
230
224
231
225 class MySQLDBBackend(DBBackend):
232 class MySQLDBBackend(DBBackend):
226 _type = 'mysql'
233 _type = 'mysql'
227
234
228 def get_default_connection_string(self):
235 def get_default_connection_string(self):
229 return 'mysql://root:qweqwe@127.0.0.1/{}'.format(self.db_name)
236 return 'mysql://root:qweqwe@127.0.0.1/{}'.format(self.db_name)
230
237
231 def setup_db(self):
238 def setup_db(self):
232 # dump schema for tests
239 # dump schema for tests
233 # mysqldump -uroot -pqweqwe $TEST_DB_NAME
240 # mysqldump -uroot -pqweqwe $TEST_DB_NAME
234 self._db_url = [{'app:main': {
241 self._db_url = [{'app:main': {
235 'sqlalchemy.db1.url': self.connection_string}}]
242 'sqlalchemy.db1.url': self.connection_string}}]
236 self.execute("mysql -v -u{} -p{} -e 'create database '{}';'".format(
243 self.execute("mysql -v -u{} -p{} -e 'create database '{}';'".format(
237 self.user, self.password, self.db_name))
244 self.user, self.password, self.db_name))
238
245
239 def import_dump(self, dumpname):
246 def import_dump(self, dumpname):
240 dump = os.path.join(self.fixture_store, dumpname)
247 dump = os.path.join(self.fixture_store, dumpname)
241 self.execute("mysql -u{} -p{} {} < {}".format(
248 self.execute("mysql -u{} -p{} {} < {}".format(
242 self.user, self.password, self.db_name, dump))
249 self.user, self.password, self.db_name, dump))
243
250
244 def teardown_db(self):
251 def teardown_db(self):
245 self.execute("mysql -v -u{} -p{} -e 'drop database '{}';'".format(
252 self.execute("mysql -v -u{} -p{} -e 'drop database '{}';'".format(
246 self.user, self.password, self.db_name))
253 self.user, self.password, self.db_name))
247
254
248
255
249 class PostgresDBBackend(DBBackend):
256 class PostgresDBBackend(DBBackend):
250 _type = 'postgres'
257 _type = 'postgres'
251
258
252 def get_default_connection_string(self):
259 def get_default_connection_string(self):
253 return 'postgresql://postgres:qweqwe@localhost/{}'.format(self.db_name)
260 return 'postgresql://postgres:qweqwe@localhost/{}'.format(self.db_name)
254
261
255 def setup_db(self):
262 def setup_db(self):
256 # dump schema for tests
263 # dump schema for tests
257 # pg_dump -U postgres -h localhost $TEST_DB_NAME
264 # pg_dump -U postgres -h localhost $TEST_DB_NAME
258 self._db_url = [{'app:main': {
265 self._db_url = [{'app:main': {
259 'sqlalchemy.db1.url':
266 'sqlalchemy.db1.url':
260 self.connection_string}}]
267 self.connection_string}}]
261 self.execute("PGPASSWORD={} psql -U {} -h localhost "
268 self.execute("PGPASSWORD={} psql -U {} -h localhost "
262 "-c 'create database '{}';'".format(
269 "-c 'create database '{}';'".format(
263 self.password, self.user, self.db_name))
270 self.password, self.user, self.db_name))
264
271
265 def teardown_db(self):
272 def teardown_db(self):
266 self.execute("PGPASSWORD={} psql -U {} -h localhost "
273 self.execute("PGPASSWORD={} psql -U {} -h localhost "
267 "-c 'drop database if exists '{}';'".format(
274 "-c 'drop database if exists '{}';'".format(
268 self.password, self.user, self.db_name))
275 self.password, self.user, self.db_name))
269
276
270 def import_dump(self, dumpname):
277 def import_dump(self, dumpname):
271 dump = os.path.join(self.fixture_store, dumpname)
278 dump = os.path.join(self.fixture_store, dumpname)
272 self.execute(
279 self.execute(
273 "PGPASSWORD={} psql -U {} -h localhost -d {} -1 "
280 "PGPASSWORD={} psql -U {} -h localhost -d {} -1 "
274 "-f {}".format(
281 "-f {}".format(
275 self.password, self.user, self.db_name, dump))
282 self.password, self.user, self.db_name, dump))
General Comments 0
You need to be logged in to leave comments. Login now