##// END OF EJS Templates
paster: fix redundant question on writable dir. The question needs to...
marcink -
r270:2b3a0761 default
parent child Browse files
Show More
@@ -1,597 +1,597 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 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)
296 296
297 297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
298 298 TEST_USER_REGULAR_EMAIL, False)
299 299
300 300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
301 301 TEST_USER_REGULAR2_EMAIL, False)
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
310 310 # Build HOOKS
311 311 hooks = [
312 312 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
313 313
314 314 # HG
315 315 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
316 316 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
317 317 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
318 318 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
319 319
320 320 ]
321 321
322 322 for key, value in hooks:
323 323 hook_obj = settings_model.get_ui_by_key(key)
324 324 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
325 325 hooks2.ui_section = 'hooks'
326 326 hooks2.ui_key = key
327 327 hooks2.ui_value = value
328 328 self.sa.add(hooks2)
329 329
330 330 # enable largefiles
331 331 largefiles = RhodeCodeUi()
332 332 largefiles.ui_section = 'extensions'
333 333 largefiles.ui_key = 'largefiles'
334 334 largefiles.ui_value = ''
335 335 self.sa.add(largefiles)
336 336
337 337 # set default largefiles cache dir, defaults to
338 338 # /repo location/.cache/largefiles
339 339 largefiles = RhodeCodeUi()
340 340 largefiles.ui_section = 'largefiles'
341 341 largefiles.ui_key = 'usercache'
342 342 largefiles.ui_value = os.path.join(repo_store_path, '.cache',
343 343 'largefiles')
344 344 self.sa.add(largefiles)
345 345
346 346 # enable hgsubversion disabled by default
347 347 hgsubversion = RhodeCodeUi()
348 348 hgsubversion.ui_section = 'extensions'
349 349 hgsubversion.ui_key = 'hgsubversion'
350 350 hgsubversion.ui_value = ''
351 351 hgsubversion.ui_active = False
352 352 self.sa.add(hgsubversion)
353 353
354 354 # enable hggit disabled by default
355 355 hggit = RhodeCodeUi()
356 356 hggit.ui_section = 'extensions'
357 357 hggit.ui_key = 'hggit'
358 358 hggit.ui_value = ''
359 359 hggit.ui_active = False
360 360 self.sa.add(hggit)
361 361
362 362 # set svn branch defaults
363 363 branches = ["/branches/*", "/trunk"]
364 364 tags = ["/tags/*"]
365 365
366 366 for branch in branches:
367 367 settings_model.create_ui_section_value(
368 368 RhodeCodeUi.SVN_BRANCH_ID, branch)
369 369
370 370 for tag in tags:
371 371 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
372 372
373 373 def create_auth_plugin_options(self, skip_existing=False):
374 374 """
375 375 Create default auth plugin settings, and make it active
376 376
377 377 :param skip_existing:
378 378 """
379 379
380 380 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
381 381 ('auth_rhodecode_enabled', 'True', 'bool')]:
382 382 if (skip_existing and
383 383 SettingsModel().get_setting_by_name(k) is not None):
384 384 log.debug('Skipping option %s' % k)
385 385 continue
386 386 setting = RhodeCodeSetting(k, v, t)
387 387 self.sa.add(setting)
388 388
389 389 def create_default_options(self, skip_existing=False):
390 390 """Creates default settings"""
391 391
392 392 for k, v, t in [
393 393 ('default_repo_enable_locking', False, 'bool'),
394 394 ('default_repo_enable_downloads', False, 'bool'),
395 395 ('default_repo_enable_statistics', False, 'bool'),
396 396 ('default_repo_private', False, 'bool'),
397 397 ('default_repo_type', 'hg', 'unicode')]:
398 398
399 399 if (skip_existing and
400 400 SettingsModel().get_setting_by_name(k) is not None):
401 401 log.debug('Skipping option %s' % k)
402 402 continue
403 403 setting = RhodeCodeSetting(k, v, t)
404 404 self.sa.add(setting)
405 405
406 406 def fixup_groups(self):
407 407 def_usr = User.get_default_user()
408 408 for g in RepoGroup.query().all():
409 409 g.group_name = g.get_new_name(g.name)
410 410 self.sa.add(g)
411 411 # get default perm
412 412 default = UserRepoGroupToPerm.query()\
413 413 .filter(UserRepoGroupToPerm.group == g)\
414 414 .filter(UserRepoGroupToPerm.user == def_usr)\
415 415 .scalar()
416 416
417 417 if default is None:
418 418 log.debug('missing default permission for group %s adding' % g)
419 419 perm_obj = RepoGroupModel()._create_default_perms(g)
420 420 self.sa.add(perm_obj)
421 421
422 422 def reset_permissions(self, username):
423 423 """
424 424 Resets permissions to default state, useful when old systems had
425 425 bad permissions, we must clean them up
426 426
427 427 :param username:
428 428 """
429 429 default_user = User.get_by_username(username)
430 430 if not default_user:
431 431 return
432 432
433 433 u2p = UserToPerm.query()\
434 434 .filter(UserToPerm.user == default_user).all()
435 435 fixed = False
436 436 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
437 437 for p in u2p:
438 438 Session().delete(p)
439 439 fixed = True
440 440 self.populate_default_permissions()
441 441 return fixed
442 442
443 443 def update_repo_info(self):
444 444 RepoModel.update_repoinfo()
445 445
446 446 def config_prompt(self, test_repo_path='', retries=3):
447 447 defaults = self.cli_args
448 448 _path = defaults.get('repos_location')
449 449 if retries == 3:
450 450 log.info('Setting up repositories config')
451 451
452 452 if _path is not None:
453 453 path = _path
454 454 elif not self.tests and not test_repo_path:
455 455 path = raw_input(
456 456 'Enter a valid absolute path to store repositories. '
457 457 'All repositories in that path will be added automatically:'
458 458 )
459 459 else:
460 460 path = test_repo_path
461 461 path_ok = True
462 462
463 463 # check proper dir
464 464 if not os.path.isdir(path):
465 465 path_ok = False
466 466 log.error('Given path %s is not a valid directory' % (path,))
467 467
468 468 elif not os.path.isabs(path):
469 469 path_ok = False
470 470 log.error('Given path %s is not an absolute path' % (path,))
471 471
472 472 # check if path is at least readable.
473 473 if not os.access(path, os.R_OK):
474 474 path_ok = False
475 475 log.error('Given path %s is not readable' % (path,))
476 476
477 477 # check write access, warn user about non writeable paths
478 478 elif not os.access(path, os.W_OK) and path_ok:
479 479 log.warning('No write permission to given path %s' % (path,))
480 480
481 q = ('Given path %s is not writeable, do you want to '
482 'continue with read only mode ? [y/n]' % (path,))
483 if not self.ask_ok(q):
481 q = ('Given path %s is not writeable, do you want to '
482 'continue with read only mode ? [y/n]' % (path,))
483 if not self.ask_ok(q):
484 484 log.error('Canceled by user')
485 485 sys.exit(-1)
486 486
487 487 if retries == 0:
488 488 sys.exit('max retries reached')
489 489 if not path_ok:
490 490 retries -= 1
491 491 return self.config_prompt(test_repo_path, retries)
492 492
493 493 real_path = os.path.normpath(os.path.realpath(path))
494 494
495 495 if real_path != os.path.normpath(path):
496 496 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
497 497 'given path as %s ? [y/n]') % (real_path,)
498 498 if not self.ask_ok(q):
499 499 log.error('Canceled by user')
500 500 sys.exit(-1)
501 501
502 502 return real_path
503 503
504 504 def create_settings(self, path):
505 505
506 506 self.create_ui_settings(path)
507 507
508 508 ui_config = [
509 509 ('web', 'push_ssl', 'false'),
510 510 ('web', 'allow_archive', 'gz zip bz2'),
511 511 ('web', 'allow_push', '*'),
512 512 ('web', 'baseurl', '/'),
513 513 ('paths', '/', path),
514 514 ('phases', 'publish', 'true')
515 515 ]
516 516 for section, key, value in ui_config:
517 517 ui_conf = RhodeCodeUi()
518 518 setattr(ui_conf, 'ui_section', section)
519 519 setattr(ui_conf, 'ui_key', key)
520 520 setattr(ui_conf, 'ui_value', value)
521 521 self.sa.add(ui_conf)
522 522
523 523 # rhodecode app settings
524 524 settings = [
525 525 ('realm', 'RhodeCode', 'unicode'),
526 526 ('title', '', 'unicode'),
527 527 ('pre_code', '', 'unicode'),
528 528 ('post_code', '', 'unicode'),
529 529 ('show_public_icon', True, 'bool'),
530 530 ('show_private_icon', True, 'bool'),
531 531 ('stylify_metatags', False, 'bool'),
532 532 ('dashboard_items', 100, 'int'),
533 533 ('admin_grid_items', 25, 'int'),
534 534 ('show_version', True, 'bool'),
535 535 ('use_gravatar', False, 'bool'),
536 536 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
537 537 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
538 538 ('support_url', '', 'unicode'),
539 539 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
540 540 ('show_revision_number', True, 'bool'),
541 541 ('show_sha_length', 12, 'int'),
542 542 ]
543 543
544 544 for key, val, type_ in settings:
545 545 sett = RhodeCodeSetting(key, val, type_)
546 546 self.sa.add(sett)
547 547
548 548 self.create_auth_plugin_options()
549 549 self.create_default_options()
550 550
551 551 log.info('created ui config')
552 552
553 553 def create_user(self, username, password, email='', admin=False,
554 554 strict_creation_check=True, api_key=None):
555 555 log.info('creating user %s' % username)
556 556 user = UserModel().create_or_update(
557 557 username, password, email, firstname='RhodeCode', lastname='Admin',
558 558 active=True, admin=admin, extern_type="rhodecode",
559 559 strict_creation_check=strict_creation_check)
560 560
561 561 if api_key:
562 562 log.info('setting a provided api key for the user %s', username)
563 563 user.api_key = api_key
564 564
565 565 def create_default_user(self):
566 566 log.info('creating default user')
567 567 # create default user for handling default permissions.
568 568 user = UserModel().create_or_update(username=User.DEFAULT_USER,
569 569 password=str(uuid.uuid1())[:20],
570 570 email=User.DEFAULT_USER_EMAIL,
571 571 firstname='Anonymous',
572 572 lastname='User',
573 573 strict_creation_check=False)
574 574 # based on configuration options activate/deactive this user which
575 575 # controlls anonymous access
576 576 if self.cli_args.get('public_access') is False:
577 577 log.info('Public access disabled')
578 578 user.active = False
579 579 Session().add(user)
580 580 Session().commit()
581 581
582 582 def create_permissions(self):
583 583 """
584 584 Creates all permissions defined in the system
585 585 """
586 586 # module.(access|create|change|delete)_[name]
587 587 # module.(none|read|write|admin)
588 588 log.info('creating permissions')
589 589 PermissionModel(self.sa).create_permissions()
590 590
591 591 def populate_default_permissions(self):
592 592 """
593 593 Populate default permissions. It will create only the default
594 594 permissions that are missing, and not alter already defined ones
595 595 """
596 596 log.info('creating default user permissions')
597 597 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
General Comments 0
You need to be logged in to leave comments. Login now