##// END OF EJS Templates
implements #507 setup-rhodecode always presents interactive prompt to destroy database, now -q flag fixes that
marcink -
r2651:957329c1 beta
parent child Browse files
Show More
@@ -1,516 +1,516 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.db_manage
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database creation, and setup module for RhodeCode. Used for creation
7 7 of database as well as for migration operations
8 8
9 9 :created_on: Apr 10, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import sys
29 29 import uuid
30 30 import logging
31 31 from os.path import dirname as dn, join as jn
32 32
33 33 from rhodecode import __dbversion__
34 34 from rhodecode.model import meta
35 35
36 36 from rhodecode.model.user import UserModel
37 37 from rhodecode.lib.utils import ask_ok
38 38 from rhodecode.model import init_model
39 39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 40 RhodeCodeSetting, UserToPerm, DbMigrateVersion, RepoGroup,\
41 41 UserRepoGroupToPerm
42 42
43 43 from sqlalchemy.engine import create_engine
44 44 from rhodecode.model.repos_group import ReposGroupModel
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class DbManage(object):
50 50 def __init__(self, log_sql, dbconf, root, tests=False):
51 51 self.dbname = dbconf.split('/')[-1]
52 52 self.tests = tests
53 53 self.root = root
54 54 self.dburi = dbconf
55 55 self.log_sql = log_sql
56 56 self.db_exists = False
57 57 self.init_db()
58 58
59 59 def init_db(self):
60 60 engine = create_engine(self.dburi, echo=self.log_sql)
61 61 init_model(engine)
62 62 self.sa = meta.Session
63 63
64 def create_tables(self, override=False):
64 def create_tables(self, override=False, defaults={}):
65 65 """
66 66 Create a auth database
67 67 """
68
68 quiet = defaults.get('quiet')
69 69 log.info("Any existing database is going to be destroyed")
70 if self.tests:
70 if self.tests or quiet:
71 71 destroy = True
72 72 else:
73 73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
74 74 if not destroy:
75 75 sys.exit()
76 76 if destroy:
77 77 meta.Base.metadata.drop_all()
78 78
79 79 checkfirst = not override
80 80 meta.Base.metadata.create_all(checkfirst=checkfirst)
81 81 log.info('Created tables for %s' % self.dbname)
82 82
83 83 def set_db_version(self):
84 84 ver = DbMigrateVersion()
85 85 ver.version = __dbversion__
86 86 ver.repository_id = 'rhodecode_db_migrations'
87 87 ver.repository_path = 'versions'
88 88 self.sa.add(ver)
89 89 log.info('db version set to: %s' % __dbversion__)
90 90
91 91 def upgrade(self):
92 92 """
93 93 Upgrades given database schema to given revision following
94 94 all needed steps, to perform the upgrade
95 95
96 96 """
97 97
98 98 from rhodecode.lib.dbmigrate.migrate.versioning import api
99 99 from rhodecode.lib.dbmigrate.migrate.exceptions import \
100 100 DatabaseNotControlledError
101 101
102 102 if 'sqlite' in self.dburi:
103 103 print (
104 104 '********************** WARNING **********************\n'
105 105 'Make sure your version of sqlite is at least 3.7.X. \n'
106 106 'Earlier versions are known to fail on some migrations\n'
107 107 '*****************************************************\n'
108 108 )
109 109 upgrade = ask_ok('You are about to perform database upgrade, make '
110 110 'sure You backed up your database before. '
111 111 'Continue ? [y/n]')
112 112 if not upgrade:
113 113 sys.exit('Nothing done')
114 114
115 115 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
116 116 'rhodecode/lib/dbmigrate')
117 117 db_uri = self.dburi
118 118
119 119 try:
120 120 curr_version = api.db_version(db_uri, repository_path)
121 121 msg = ('Found current database under version'
122 122 ' control with version %s' % curr_version)
123 123
124 124 except (RuntimeError, DatabaseNotControlledError):
125 125 curr_version = 1
126 126 msg = ('Current database is not under version control. Setting'
127 127 ' as version %s' % curr_version)
128 128 api.version_control(db_uri, repository_path, curr_version)
129 129
130 130 print (msg)
131 131
132 132 if curr_version == __dbversion__:
133 133 sys.exit('This database is already at the newest version')
134 134
135 135 #======================================================================
136 136 # UPGRADE STEPS
137 137 #======================================================================
138 138 class UpgradeSteps(object):
139 139 """
140 140 Those steps follow schema versions so for example schema
141 141 for example schema with seq 002 == step_2 and so on.
142 142 """
143 143
144 144 def __init__(self, klass):
145 145 self.klass = klass
146 146
147 147 def step_0(self):
148 148 # step 0 is the schema upgrade, and than follow proper upgrades
149 149 print ('attempting to do database upgrade to version %s' \
150 150 % __dbversion__)
151 151 api.upgrade(db_uri, repository_path, __dbversion__)
152 152 print ('Schema upgrade completed')
153 153
154 154 def step_1(self):
155 155 pass
156 156
157 157 def step_2(self):
158 158 print ('Patching repo paths for newer version of RhodeCode')
159 159 self.klass.fix_repo_paths()
160 160
161 161 print ('Patching default user of RhodeCode')
162 162 self.klass.fix_default_user()
163 163
164 164 log.info('Changing ui settings')
165 165 self.klass.create_ui_settings()
166 166
167 167 def step_3(self):
168 168 print ('Adding additional settings into RhodeCode db')
169 169 self.klass.fix_settings()
170 170 print ('Adding ldap defaults')
171 171 self.klass.create_ldap_options(skip_existing=True)
172 172
173 173 def step_4(self):
174 174 print ('create permissions and fix groups')
175 175 self.klass.create_permissions()
176 176 self.klass.fixup_groups()
177 177
178 178 def step_5(self):
179 179 pass
180 180
181 181 def step_6(self):
182 182 pass
183 183 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
184 184
185 185 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
186 186 for step in upgrade_steps:
187 187 print ('performing upgrade step %s' % step)
188 188 getattr(UpgradeSteps(self), 'step_%s' % step)()
189 189 self.sa.commit()
190 190
191 191 def fix_repo_paths(self):
192 192 """
193 193 Fixes a old rhodecode version path into new one without a '*'
194 194 """
195 195
196 196 paths = self.sa.query(RhodeCodeUi)\
197 197 .filter(RhodeCodeUi.ui_key == '/')\
198 198 .scalar()
199 199
200 200 paths.ui_value = paths.ui_value.replace('*', '')
201 201
202 202 try:
203 203 self.sa.add(paths)
204 204 self.sa.commit()
205 205 except:
206 206 self.sa.rollback()
207 207 raise
208 208
209 209 def fix_default_user(self):
210 210 """
211 211 Fixes a old default user with some 'nicer' default values,
212 212 used mostly for anonymous access
213 213 """
214 214 def_user = self.sa.query(User)\
215 215 .filter(User.username == 'default')\
216 216 .one()
217 217
218 218 def_user.name = 'Anonymous'
219 219 def_user.lastname = 'User'
220 220 def_user.email = 'anonymous@rhodecode.org'
221 221
222 222 try:
223 223 self.sa.add(def_user)
224 224 self.sa.commit()
225 225 except:
226 226 self.sa.rollback()
227 227 raise
228 228
229 229 def fix_settings(self):
230 230 """
231 231 Fixes rhodecode settings adds ga_code key for google analytics
232 232 """
233 233
234 234 hgsettings3 = RhodeCodeSetting('ga_code', '')
235 235
236 236 try:
237 237 self.sa.add(hgsettings3)
238 238 self.sa.commit()
239 239 except:
240 240 self.sa.rollback()
241 241 raise
242 242
243 243 def admin_prompt(self, second=False, defaults={}):
244 244 if not self.tests:
245 245 import getpass
246 246
247 247 # defaults
248 248 username = defaults.get('username')
249 249 password = defaults.get('password')
250 250 email = defaults.get('email')
251 251
252 252 def get_password():
253 253 password = getpass.getpass('Specify admin password '
254 254 '(min 6 chars):')
255 255 confirm = getpass.getpass('Confirm password:')
256 256
257 257 if password != confirm:
258 258 log.error('passwords mismatch')
259 259 return False
260 260 if len(password) < 6:
261 261 log.error('password is to short use at least 6 characters')
262 262 return False
263 263
264 264 return password
265 265 if username is None:
266 266 username = raw_input('Specify admin username:')
267 267 if password is None:
268 268 password = get_password()
269 269 if not password:
270 270 #second try
271 271 password = get_password()
272 272 if not password:
273 273 sys.exit()
274 274 if email is None:
275 275 email = raw_input('Specify admin email:')
276 276 self.create_user(username, password, email, True)
277 277 else:
278 278 log.info('creating admin and regular test users')
279 279 from rhodecode.tests import TEST_USER_ADMIN_LOGIN,\
280 280 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL,\
281 281 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,\
282 282 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
283 283 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
284 284
285 285 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
286 286 TEST_USER_ADMIN_EMAIL, True)
287 287
288 288 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
289 289 TEST_USER_REGULAR_EMAIL, False)
290 290
291 291 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
292 292 TEST_USER_REGULAR2_EMAIL, False)
293 293
294 294 def create_ui_settings(self):
295 295 """
296 296 Creates ui settings, fills out hooks
297 297 and disables dotencode
298 298 """
299 299
300 300 #HOOKS
301 301 hooks1_key = RhodeCodeUi.HOOK_UPDATE
302 302 hooks1_ = self.sa.query(RhodeCodeUi)\
303 303 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
304 304
305 305 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
306 306 hooks1.ui_section = 'hooks'
307 307 hooks1.ui_key = hooks1_key
308 308 hooks1.ui_value = 'hg update >&2'
309 309 hooks1.ui_active = False
310 310
311 311 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
312 312 hooks2_ = self.sa.query(RhodeCodeUi)\
313 313 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
314 314
315 315 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
316 316 hooks2.ui_section = 'hooks'
317 317 hooks2.ui_key = hooks2_key
318 318 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
319 319
320 320 hooks3 = RhodeCodeUi()
321 321 hooks3.ui_section = 'hooks'
322 322 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
323 323 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
324 324
325 325 hooks4 = RhodeCodeUi()
326 326 hooks4.ui_section = 'hooks'
327 327 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
328 328 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
329 329
330 330 # For mercurial 1.7 set backward comapatibility with format
331 331 dotencode_disable = RhodeCodeUi()
332 332 dotencode_disable.ui_section = 'format'
333 333 dotencode_disable.ui_key = 'dotencode'
334 334 dotencode_disable.ui_value = 'false'
335 335
336 336 # enable largefiles
337 337 largefiles = RhodeCodeUi()
338 338 largefiles.ui_section = 'extensions'
339 339 largefiles.ui_key = 'largefiles'
340 340 largefiles.ui_value = ''
341 341
342 342 self.sa.add(hooks1)
343 343 self.sa.add(hooks2)
344 344 self.sa.add(hooks3)
345 345 self.sa.add(hooks4)
346 346 self.sa.add(largefiles)
347 347
348 348 def create_ldap_options(self, skip_existing=False):
349 349 """Creates ldap settings"""
350 350
351 351 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
352 352 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
353 353 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
354 354 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
355 355 ('ldap_filter', ''), ('ldap_search_scope', ''),
356 356 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
357 357 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
358 358
359 359 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
360 360 log.debug('Skipping option %s' % k)
361 361 continue
362 362 setting = RhodeCodeSetting(k, v)
363 363 self.sa.add(setting)
364 364
365 365 def fixup_groups(self):
366 366 def_usr = User.get_by_username('default')
367 367 for g in RepoGroup.query().all():
368 368 g.group_name = g.get_new_name(g.name)
369 369 self.sa.add(g)
370 370 # get default perm
371 371 default = UserRepoGroupToPerm.query()\
372 372 .filter(UserRepoGroupToPerm.group == g)\
373 373 .filter(UserRepoGroupToPerm.user == def_usr)\
374 374 .scalar()
375 375
376 376 if default is None:
377 377 log.debug('missing default permission for group %s adding' % g)
378 378 ReposGroupModel()._create_default_perms(g)
379 379
380 380 def config_prompt(self, test_repo_path='', retries=3, defaults={}):
381 381 _path = defaults.get('repos_location')
382 382 if retries == 3:
383 383 log.info('Setting up repositories config')
384 384
385 385 if _path is not None:
386 386 path = _path
387 387 elif not self.tests and not test_repo_path:
388 388 path = raw_input(
389 389 'Enter a valid absolute path to store repositories. '
390 390 'All repositories in that path will be added automatically:'
391 391 )
392 392 else:
393 393 path = test_repo_path
394 394 path_ok = True
395 395
396 396 # check proper dir
397 397 if not os.path.isdir(path):
398 398 path_ok = False
399 399 log.error('Given path %s is not a valid directory' % path)
400 400
401 401 elif not os.path.isabs(path):
402 402 path_ok = False
403 403 log.error('Given path %s is not an absolute path' % path)
404 404
405 405 # check write access
406 406 elif not os.access(path, os.W_OK) and path_ok:
407 407 path_ok = False
408 408 log.error('No write permission to given path %s' % path)
409 409
410 410 if retries == 0:
411 411 sys.exit('max retries reached')
412 412 if path_ok is False:
413 413 retries -= 1
414 414 return self.config_prompt(test_repo_path, retries)
415 415
416 416 return path
417 417
418 418 def create_settings(self, path):
419 419
420 420 self.create_ui_settings()
421 421
422 422 #HG UI OPTIONS
423 423 web1 = RhodeCodeUi()
424 424 web1.ui_section = 'web'
425 425 web1.ui_key = 'push_ssl'
426 426 web1.ui_value = 'false'
427 427
428 428 web2 = RhodeCodeUi()
429 429 web2.ui_section = 'web'
430 430 web2.ui_key = 'allow_archive'
431 431 web2.ui_value = 'gz zip bz2'
432 432
433 433 web3 = RhodeCodeUi()
434 434 web3.ui_section = 'web'
435 435 web3.ui_key = 'allow_push'
436 436 web3.ui_value = '*'
437 437
438 438 web4 = RhodeCodeUi()
439 439 web4.ui_section = 'web'
440 440 web4.ui_key = 'baseurl'
441 441 web4.ui_value = '/'
442 442
443 443 paths = RhodeCodeUi()
444 444 paths.ui_section = 'paths'
445 445 paths.ui_key = '/'
446 446 paths.ui_value = path
447 447
448 448 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
449 449 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
450 450 hgsettings3 = RhodeCodeSetting('ga_code', '')
451 451
452 452 self.sa.add(web1)
453 453 self.sa.add(web2)
454 454 self.sa.add(web3)
455 455 self.sa.add(web4)
456 456 self.sa.add(paths)
457 457 self.sa.add(hgsettings1)
458 458 self.sa.add(hgsettings2)
459 459 self.sa.add(hgsettings3)
460 460
461 461 self.create_ldap_options()
462 462
463 463 log.info('created ui config')
464 464
465 465 def create_user(self, username, password, email='', admin=False):
466 466 log.info('creating user %s' % username)
467 467 UserModel().create_or_update(username, password, email,
468 468 firstname='RhodeCode', lastname='Admin',
469 469 active=True, admin=admin)
470 470
471 471 def create_default_user(self):
472 472 log.info('creating default user')
473 473 # create default user for handling default permissions.
474 474 UserModel().create_or_update(username='default',
475 475 password=str(uuid.uuid1())[:8],
476 476 email='anonymous@rhodecode.org',
477 477 firstname='Anonymous', lastname='User')
478 478
479 479 def create_permissions(self):
480 480 # module.(access|create|change|delete)_[name]
481 481 # module.(none|read|write|admin)
482 482
483 483 for p in Permission.PERMS:
484 484 if not Permission.get_by_key(p[0]):
485 485 new_perm = Permission()
486 486 new_perm.permission_name = p[0]
487 487 new_perm.permission_longname = p[0]
488 488 self.sa.add(new_perm)
489 489
490 490 def populate_default_permissions(self):
491 491 log.info('creating default user permissions')
492 492
493 493 default_user = self.sa.query(User)\
494 494 .filter(User.username == 'default').scalar()
495 495
496 496 reg_perm = UserToPerm()
497 497 reg_perm.user = default_user
498 498 reg_perm.permission = self.sa.query(Permission)\
499 499 .filter(Permission.permission_name == 'hg.register.manual_activate')\
500 500 .scalar()
501 501
502 502 create_repo_perm = UserToPerm()
503 503 create_repo_perm.user = default_user
504 504 create_repo_perm.permission = self.sa.query(Permission)\
505 505 .filter(Permission.permission_name == 'hg.create.repository')\
506 506 .scalar()
507 507
508 508 default_repo_perm = UserToPerm()
509 509 default_repo_perm.user = default_user
510 510 default_repo_perm.permission = self.sa.query(Permission)\
511 511 .filter(Permission.permission_name == 'repository.read')\
512 512 .scalar()
513 513
514 514 self.sa.add(reg_perm)
515 515 self.sa.add(create_repo_perm)
516 516 self.sa.add(default_repo_perm)
@@ -1,50 +1,50 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.websetup
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Weboperations and setup for rhodecode
7 7
8 8 :created_on: Dec 11, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from rhodecode.config.environment import load_environment
29 29 from rhodecode.lib.db_manage import DbManage
30 30 from rhodecode.model.meta import Session
31 31
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35
36 36 def setup_app(command, conf, vars):
37 37 """Place any commands to setup rhodecode here"""
38 38 dbconf = conf['sqlalchemy.db1.url']
39 39 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'],
40 40 tests=False)
41 dbmanage.create_tables(override=True)
41 dbmanage.create_tables(override=True, defaults=command.options.__dict__)
42 42 dbmanage.set_db_version()
43 43 opts = dbmanage.config_prompt(None, defaults=command.options.__dict__)
44 44 dbmanage.create_settings(opts)
45 45 dbmanage.create_default_user()
46 46 dbmanage.admin_prompt(defaults=command.options.__dict__)
47 47 dbmanage.create_permissions()
48 48 dbmanage.populate_default_permissions()
49 49 Session.commit()
50 50 load_environment(conf.global_conf, conf.local_conf, initial=True)
General Comments 0
You need to be logged in to leave comments. Login now