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