##// END OF EJS Templates
upgrade-db command dummy
marcink -
r684:2abb398c beta
parent child Browse files
Show More
@@ -1,519 +1,544
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 """
20 20 Created on April 18, 2010
21 21 Utilities for RhodeCode
22 22 @author: marcink
23 23 """
24 24
25 25 from UserDict import DictMixin
26 26 from mercurial import ui, config, hg
27 27 from mercurial.error import RepoError
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.caching_query import FromCache
30 30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
31 31 UserLog
32 32 from rhodecode.model.repo import RepoModel
33 33 from rhodecode.model.user import UserModel
34 34 from vcs.backends.base import BaseChangeset
35 from vcs.backends.git import GitRepository
36 from vcs.backends.hg import MercurialRepository
35 from paste.script import command
36 import ConfigParser
37 37 from vcs.utils.lazy import LazyProperty
38 38 import traceback
39 39 import datetime
40 40 import logging
41 41 import os
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 def get_repo_slug(request):
47 47 return request.environ['pylons.routes_dict'].get('repo_name')
48 48
49 49 def is_mercurial(environ):
50 50 """
51 51 Returns True if request's target is mercurial server - header
52 52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 53 """
54 54 http_accept = environ.get('HTTP_ACCEPT')
55 55 if http_accept and http_accept.startswith('application/mercurial'):
56 56 return True
57 57 return False
58 58
59 59 def is_git(environ):
60 60 """
61 61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
62 62 then have git client version given.
63 63
64 64 :param environ:
65 65 """
66 66 http_user_agent = environ.get('HTTP_USER_AGENT')
67 67 if http_user_agent.startswith('git'):
68 68 return True
69 69 return False
70 70
71 71 def action_logger(user, action, repo, ipaddr, sa=None):
72 72 """
73 73 Action logger for various action made by users
74 74 """
75 75
76 76 if not sa:
77 77 sa = meta.Session()
78 78
79 79 try:
80 80 if hasattr(user, 'user_id'):
81 81 user_obj = user
82 82 elif isinstance(user, basestring):
83 83 user_obj = UserModel(sa).get_by_username(user, cache=False)
84 84 else:
85 85 raise Exception('You have to provide user object or username')
86 86
87 87 repo_name = repo.lstrip('/')
88 88 user_log = UserLog()
89 89 user_log.user_id = user_obj.user_id
90 90 user_log.action = action
91 91 user_log.repository_name = repo_name
92 92 user_log.repository = RepoModel(sa).get(repo_name, cache=False)
93 93 user_log.action_date = datetime.datetime.now()
94 94 user_log.user_ip = ipaddr
95 95 sa.add(user_log)
96 96 sa.commit()
97 97
98 98 log.info('Adding user %s, action %s on %s',
99 99 user_obj.username, action, repo)
100 100 except:
101 101 log.error(traceback.format_exc())
102 102 sa.rollback()
103 103
104 104 def get_repos(path, recursive=False, initial=False):
105 105 """
106 106 Scans given path for repos and return (name,(type,path)) tuple
107 107 :param prefix:
108 108 :param path:
109 109 :param recursive:
110 110 :param initial:
111 111 """
112 112 from vcs.utils.helpers import get_scm
113 113 from vcs.exceptions import VCSError
114 114
115 115 try:
116 116 scm = get_scm(path)
117 117 except:
118 118 pass
119 119 else:
120 120 raise Exception('The given path %s should not be a repository got %s',
121 121 path, scm)
122 122
123 123 for dirpath in os.listdir(path):
124 124 try:
125 125 yield dirpath, get_scm(os.path.join(path, dirpath))
126 126 except VCSError:
127 127 pass
128 128
129 129 if __name__ == '__main__':
130 130 get_repos('', '/home/marcink/workspace-python')
131 131
132 132
133 133 def check_repo_fast(repo_name, base_path):
134 134 if os.path.isdir(os.path.join(base_path, repo_name)):return False
135 135 return True
136 136
137 137 def check_repo(repo_name, base_path, verify=True):
138 138
139 139 repo_path = os.path.join(base_path, repo_name)
140 140
141 141 try:
142 142 if not check_repo_fast(repo_name, base_path):
143 143 return False
144 144 r = hg.repository(ui.ui(), repo_path)
145 145 if verify:
146 146 hg.verify(r)
147 147 #here we hnow that repo exists it was verified
148 148 log.info('%s repo is already created', repo_name)
149 149 return False
150 150 except RepoError:
151 151 #it means that there is no valid repo there...
152 152 log.info('%s repo is free for creation', repo_name)
153 153 return True
154 154
155 155 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
156 156 while True:
157 157 ok = raw_input(prompt)
158 158 if ok in ('y', 'ye', 'yes'): return True
159 159 if ok in ('n', 'no', 'nop', 'nope'): return False
160 160 retries = retries - 1
161 161 if retries < 0: raise IOError
162 162 print complaint
163 163
164 164 def get_hg_ui_cached():
165 165 try:
166 166 sa = meta.Session
167 167 ret = sa.query(RhodeCodeUi)\
168 168 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
169 169 .all()
170 170 except:
171 171 pass
172 172 finally:
173 173 meta.Session.remove()
174 174 return ret
175 175
176 176
177 177 def get_hg_settings():
178 178 try:
179 179 sa = meta.Session()
180 180 ret = sa.query(RhodeCodeSettings)\
181 181 .options(FromCache("sql_cache_short", "get_hg_settings"))\
182 182 .all()
183 183 except:
184 184 pass
185 185 finally:
186 186 meta.Session.remove()
187 187
188 188 if not ret:
189 189 raise Exception('Could not get application settings !')
190 190 settings = {}
191 191 for each in ret:
192 192 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
193 193
194 194 return settings
195 195
196 196 def get_hg_ui_settings():
197 197 try:
198 198 sa = meta.Session()
199 199 ret = sa.query(RhodeCodeUi).all()
200 200 except:
201 201 pass
202 202 finally:
203 203 meta.Session.remove()
204 204
205 205 if not ret:
206 206 raise Exception('Could not get application ui settings !')
207 207 settings = {}
208 208 for each in ret:
209 209 k = each.ui_key
210 210 v = each.ui_value
211 211 if k == '/':
212 212 k = 'root_path'
213 213
214 214 if k.find('.') != -1:
215 215 k = k.replace('.', '_')
216 216
217 217 if each.ui_section == 'hooks':
218 218 v = each.ui_active
219 219
220 220 settings[each.ui_section + '_' + k] = v
221 221
222 222 return settings
223 223
224 224 #propagated from mercurial documentation
225 225 ui_sections = ['alias', 'auth',
226 226 'decode/encode', 'defaults',
227 227 'diff', 'email',
228 228 'extensions', 'format',
229 229 'merge-patterns', 'merge-tools',
230 230 'hooks', 'http_proxy',
231 231 'smtp', 'patch',
232 232 'paths', 'profiling',
233 233 'server', 'trusted',
234 234 'ui', 'web', ]
235 235
236 236 def make_ui(read_from='file', path=None, checkpaths=True):
237 237 """
238 238 A function that will read python rc files or database
239 239 and make an mercurial ui object from read options
240 240
241 241 :param path: path to mercurial config file
242 242 :param checkpaths: check the path
243 243 :param read_from: read from 'file' or 'db'
244 244 """
245 245
246 246 baseui = ui.ui()
247 247
248 248 if read_from == 'file':
249 249 if not os.path.isfile(path):
250 250 log.warning('Unable to read config file %s' % path)
251 251 return False
252 252 log.debug('reading hgrc from %s', path)
253 253 cfg = config.config()
254 254 cfg.read(path)
255 255 for section in ui_sections:
256 256 for k, v in cfg.items(section):
257 257 baseui.setconfig(section, k, v)
258 258 log.debug('settings ui from file[%s]%s:%s', section, k, v)
259 259
260 260 elif read_from == 'db':
261 261 hg_ui = get_hg_ui_cached()
262 262 for ui_ in hg_ui:
263 263 if ui_.ui_active:
264 264 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
265 265 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
266 266
267 267
268 268 return baseui
269 269
270 270
271 271 def set_rhodecode_config(config):
272 272 hgsettings = get_hg_settings()
273 273
274 274 for k, v in hgsettings.items():
275 275 config[k] = v
276 276
277 277 def invalidate_cache(name, *args):
278 278 """
279 279 Puts cache invalidation task into db for
280 280 further global cache invalidation
281 281 """
282 282 pass
283 283
284 284 class EmptyChangeset(BaseChangeset):
285 285 """
286 286 An dummy empty changeset. It's possible to pass hash when creating
287 287 an EmptyChangeset
288 288 """
289 289
290 290 def __init__(self, cs='0' * 40):
291 291 self._empty_cs = cs
292 292 self.revision = -1
293 293 self.message = ''
294 294 self.author = ''
295 295 self.date = ''
296 296
297 297 @LazyProperty
298 298 def raw_id(self):
299 299 """
300 300 Returns raw string identifying this changeset, useful for web
301 301 representation.
302 302 """
303 303 return self._empty_cs
304 304
305 305 @LazyProperty
306 306 def short_id(self):
307 307 return self.raw_id[:12]
308 308
309 309 def get_file_changeset(self, path):
310 310 return self
311 311
312 312 def get_file_content(self, path):
313 313 return u''
314 314
315 315 def get_file_size(self, path):
316 316 return 0
317 317
318 318 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
319 319 """
320 320 maps all found repositories into db
321 321 """
322 322
323 323 sa = meta.Session()
324 324 rm = RepoModel(sa)
325 325 user = sa.query(User).filter(User.admin == True).first()
326 326
327 327 for name, repo in initial_repo_list.items():
328 328 if not rm.get(name, cache=False):
329 329 log.info('repository %s not found creating default', name)
330 330
331 331 form_data = {
332 332 'repo_name':name,
333 333 'repo_type':repo.alias,
334 334 'description':repo.description \
335 335 if repo.description != 'unknown' else \
336 336 '%s repository' % name,
337 337 'private':False
338 338 }
339 339 rm.create(form_data, user, just_db=True)
340 340
341 341 if remove_obsolete:
342 342 #remove from database those repositories that are not in the filesystem
343 343 for repo in sa.query(Repository).all():
344 344 if repo.repo_name not in initial_repo_list.keys():
345 345 sa.delete(repo)
346 346 sa.commit()
347 347
348 348 class OrderedDict(dict, DictMixin):
349 349
350 350 def __init__(self, *args, **kwds):
351 351 if len(args) > 1:
352 352 raise TypeError('expected at most 1 arguments, got %d' % len(args))
353 353 try:
354 354 self.__end
355 355 except AttributeError:
356 356 self.clear()
357 357 self.update(*args, **kwds)
358 358
359 359 def clear(self):
360 360 self.__end = end = []
361 361 end += [None, end, end] # sentinel node for doubly linked list
362 362 self.__map = {} # key --> [key, prev, next]
363 363 dict.clear(self)
364 364
365 365 def __setitem__(self, key, value):
366 366 if key not in self:
367 367 end = self.__end
368 368 curr = end[1]
369 369 curr[2] = end[1] = self.__map[key] = [key, curr, end]
370 370 dict.__setitem__(self, key, value)
371 371
372 372 def __delitem__(self, key):
373 373 dict.__delitem__(self, key)
374 374 key, prev, next = self.__map.pop(key)
375 375 prev[2] = next
376 376 next[1] = prev
377 377
378 378 def __iter__(self):
379 379 end = self.__end
380 380 curr = end[2]
381 381 while curr is not end:
382 382 yield curr[0]
383 383 curr = curr[2]
384 384
385 385 def __reversed__(self):
386 386 end = self.__end
387 387 curr = end[1]
388 388 while curr is not end:
389 389 yield curr[0]
390 390 curr = curr[1]
391 391
392 392 def popitem(self, last=True):
393 393 if not self:
394 394 raise KeyError('dictionary is empty')
395 395 if last:
396 396 key = reversed(self).next()
397 397 else:
398 398 key = iter(self).next()
399 399 value = self.pop(key)
400 400 return key, value
401 401
402 402 def __reduce__(self):
403 403 items = [[k, self[k]] for k in self]
404 404 tmp = self.__map, self.__end
405 405 del self.__map, self.__end
406 406 inst_dict = vars(self).copy()
407 407 self.__map, self.__end = tmp
408 408 if inst_dict:
409 409 return (self.__class__, (items,), inst_dict)
410 410 return self.__class__, (items,)
411 411
412 412 def keys(self):
413 413 return list(self)
414 414
415 415 setdefault = DictMixin.setdefault
416 416 update = DictMixin.update
417 417 pop = DictMixin.pop
418 418 values = DictMixin.values
419 419 items = DictMixin.items
420 420 iterkeys = DictMixin.iterkeys
421 421 itervalues = DictMixin.itervalues
422 422 iteritems = DictMixin.iteritems
423 423
424 424 def __repr__(self):
425 425 if not self:
426 426 return '%s()' % (self.__class__.__name__,)
427 427 return '%s(%r)' % (self.__class__.__name__, self.items())
428 428
429 429 def copy(self):
430 430 return self.__class__(self)
431 431
432 432 @classmethod
433 433 def fromkeys(cls, iterable, value=None):
434 434 d = cls()
435 435 for key in iterable:
436 436 d[key] = value
437 437 return d
438 438
439 439 def __eq__(self, other):
440 440 if isinstance(other, OrderedDict):
441 441 return len(self) == len(other) and self.items() == other.items()
442 442 return dict.__eq__(self, other)
443 443
444 444 def __ne__(self, other):
445 445 return not self == other
446 446
447 447
448 448 #===============================================================================
449 449 # TEST FUNCTIONS AND CREATORS
450 450 #===============================================================================
451 451 def create_test_index(repo_location, full_index):
452 452 """Makes default test index
453 453 :param repo_location:
454 454 :param full_index:
455 455 """
456 456 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
457 457 from rhodecode.lib.pidlock import DaemonLock, LockHeld
458 458 from rhodecode.lib.indexers import IDX_LOCATION
459 459 import shutil
460 460
461 461 if os.path.exists(IDX_LOCATION):
462 462 shutil.rmtree(IDX_LOCATION)
463 463
464 464 try:
465 465 l = DaemonLock()
466 466 WhooshIndexingDaemon(repo_location=repo_location)\
467 467 .run(full_index=full_index)
468 468 l.release()
469 469 except LockHeld:
470 470 pass
471 471
472 472 def create_test_env(repos_test_path, config):
473 473 """Makes a fresh database and
474 474 install test repository into tmp dir
475 475 """
476 476 from rhodecode.lib.db_manage import DbManage
477 477 import tarfile
478 478 import shutil
479 479 from os.path import dirname as dn, join as jn, abspath
480 480
481 481 log = logging.getLogger('TestEnvCreator')
482 482 # create logger
483 483 log.setLevel(logging.DEBUG)
484 484 log.propagate = True
485 485 # create console handler and set level to debug
486 486 ch = logging.StreamHandler()
487 487 ch.setLevel(logging.DEBUG)
488 488
489 489 # create formatter
490 490 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
491 491
492 492 # add formatter to ch
493 493 ch.setFormatter(formatter)
494 494
495 495 # add ch to logger
496 496 log.addHandler(ch)
497 497
498 498 #PART ONE create db
499 499 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
500 500 log.debug('making test db %s', dbname)
501 501
502 502 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
503 503 tests=True)
504 504 dbmanage.create_tables(override=True)
505 505 dbmanage.config_prompt(repos_test_path)
506 506 dbmanage.create_default_user()
507 507 dbmanage.admin_prompt()
508 508 dbmanage.create_permissions()
509 509 dbmanage.populate_default_permissions()
510 510
511 511 #PART TWO make test repo
512 512 log.debug('making test vcs repo')
513 513 if os.path.isdir('/tmp/vcs_test'):
514 514 shutil.rmtree('/tmp/vcs_test')
515 515
516 516 cur_dir = dn(dn(abspath(__file__)))
517 517 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
518 518 tar.extractall('/tmp')
519 519 tar.close()
520
521 class UpgradeDb(command.Command):
522 """Command used for paster to upgrade our database to newer version
523 """
524
525 max_args = 1
526 min_args = 1
527
528 usage = "CONFIG_FILE"
529 summary = "Upgrades current db to newer version given configuration file"
530 group_name = "RhodeCode"
531
532 parser = command.Command.standard_parser(verbose=True)
533
534 parser.add_option('--sql',
535 action='store_true',
536 dest='just_sql',
537 help="Prints upgrade sql for further investigation",
538 default=False)
539 def command(self):
540 config_name = self.args[0]
541 p = config_name.split('/')
542 root = '.' if len(p) == 1 else '/'.join(p[:-1])
543 config = ConfigParser.ConfigParser({'here':root})
544 config.read(config_name)
General Comments 0
You need to be logged in to leave comments. Login now