##// END OF EJS Templates
fixed issue with db transaction when filesystem creation of repository failed...
marcink -
r1028:f42ee60e beta
parent child Browse files
Show More
@@ -1,330 +1,331 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import shutil
28 import shutil
29 import logging
29 import logging
30 import traceback
30 import traceback
31 from datetime import datetime
31 from datetime import datetime
32
32
33 from rhodecode.model import BaseModel
33 from rhodecode.model import BaseModel
34 from rhodecode.model.caching_query import FromCache
34 from rhodecode.model.caching_query import FromCache
35 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
35 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
36 Statistics, UsersGroup, UsersGroupToPerm
36 Statistics, UsersGroup, UsersGroupToPerm
37 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
38 from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
38 from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
39
39
40 from vcs.backends import get_backend
40 from vcs.backends import get_backend
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 class RepoModel(BaseModel):
44 class RepoModel(BaseModel):
45
45
46 def __init__(self, sa=None):
46 def __init__(self, sa=None):
47 try:
47 try:
48 from pylons import app_globals
48 from pylons import app_globals
49 self._base_path = app_globals.base_path
49 self._base_path = app_globals.base_path
50 except:
50 except:
51 self._base_path = None
51 self._base_path = None
52
52
53 super(RepoModel, self).__init__(sa)
53 super(RepoModel, self).__init__(sa)
54
54
55 @property
55 @property
56 def base_path(self):
56 def base_path(self):
57 if self._base_path is None:
57 if self._base_path is None:
58 raise Exception('Base Path is empty, try set this after'
58 raise Exception('Base Path is empty, try set this after'
59 'class initialization when not having '
59 'class initialization when not having '
60 'app_globals available')
60 'app_globals available')
61 return self._base_path
61 return self._base_path
62
62
63 super(RepoModel, self).__init__()
63 super(RepoModel, self).__init__()
64
64
65
65
66 def get(self, repo_id, cache=False):
66 def get(self, repo_id, cache=False):
67 repo = self.sa.query(Repository)\
67 repo = self.sa.query(Repository)\
68 .filter(Repository.repo_id == repo_id)
68 .filter(Repository.repo_id == repo_id)
69
69
70 if cache:
70 if cache:
71 repo = repo.options(FromCache("sql_cache_short",
71 repo = repo.options(FromCache("sql_cache_short",
72 "get_repo_%s" % repo_id))
72 "get_repo_%s" % repo_id))
73 return repo.scalar()
73 return repo.scalar()
74
74
75
75
76 def get_by_repo_name(self, repo_name, cache=False):
76 def get_by_repo_name(self, repo_name, cache=False):
77 repo = self.sa.query(Repository)\
77 repo = self.sa.query(Repository)\
78 .filter(Repository.repo_name == repo_name)
78 .filter(Repository.repo_name == repo_name)
79
79
80 if cache:
80 if cache:
81 repo = repo.options(FromCache("sql_cache_short",
81 repo = repo.options(FromCache("sql_cache_short",
82 "get_repo_%s" % repo_name))
82 "get_repo_%s" % repo_name))
83 return repo.scalar()
83 return repo.scalar()
84
84
85 def get_users_js(self):
85 def get_users_js(self):
86
86
87 users = self.sa.query(User).filter(User.active == True).all()
87 users = self.sa.query(User).filter(User.active == True).all()
88 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
88 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
89 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
89 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
90 u.lastname, u.username)
90 u.lastname, u.username)
91 for u in users])
91 for u in users])
92 return users_array
92 return users_array
93
93
94
94
95 def get_users_groups_js(self):
95 def get_users_groups_js(self):
96 users_groups = self.sa.query(UsersGroup)\
96 users_groups = self.sa.query(UsersGroup)\
97 .filter(UsersGroup.users_group_active == True).all()
97 .filter(UsersGroup.users_group_active == True).all()
98
98
99 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
99 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
100
100
101 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
101 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
102 (gr.users_group_id, gr.users_group_name,
102 (gr.users_group_id, gr.users_group_name,
103 len(gr.members))
103 len(gr.members))
104 for gr in users_groups])
104 for gr in users_groups])
105 return users_groups_array
105 return users_groups_array
106
106
107 def update(self, repo_name, form_data):
107 def update(self, repo_name, form_data):
108 try:
108 try:
109 cur_repo = self.get_by_repo_name(repo_name, cache=False)
109 cur_repo = self.get_by_repo_name(repo_name, cache=False)
110 user_model = UserModel(self.sa)
110 user_model = UserModel(self.sa)
111 users_group_model = UsersGroupModel(self.sa)
111 users_group_model = UsersGroupModel(self.sa)
112
112
113 #update permissions
113 #update permissions
114 for member, perm, member_type in form_data['perms_updates']:
114 for member, perm, member_type in form_data['perms_updates']:
115 if member_type == 'user':
115 if member_type == 'user':
116 r2p = self.sa.query(RepoToPerm)\
116 r2p = self.sa.query(RepoToPerm)\
117 .filter(RepoToPerm.user == user_model.get_by_username(member))\
117 .filter(RepoToPerm.user == user_model.get_by_username(member))\
118 .filter(RepoToPerm.repository == cur_repo)\
118 .filter(RepoToPerm.repository == cur_repo)\
119 .one()
119 .one()
120
120
121 r2p.permission = self.sa.query(Permission)\
121 r2p.permission = self.sa.query(Permission)\
122 .filter(Permission.permission_name == perm)\
122 .filter(Permission.permission_name == perm)\
123 .scalar()
123 .scalar()
124 self.sa.add(r2p)
124 self.sa.add(r2p)
125 else:
125 else:
126 g2p = self.sa.query(UsersGroupToPerm)\
126 g2p = self.sa.query(UsersGroupToPerm)\
127 .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
127 .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
128 .filter(UsersGroupToPerm.repository == cur_repo)\
128 .filter(UsersGroupToPerm.repository == cur_repo)\
129 .one()
129 .one()
130
130
131 g2p.permission = self.sa.query(Permission)\
131 g2p.permission = self.sa.query(Permission)\
132 .filter(Permission.permission_name == perm)\
132 .filter(Permission.permission_name == perm)\
133 .scalar()
133 .scalar()
134 self.sa.add(g2p)
134 self.sa.add(g2p)
135
135
136 #set new permissions
136 #set new permissions
137 for member, perm, member_type in form_data['perms_new']:
137 for member, perm, member_type in form_data['perms_new']:
138 if member_type == 'user':
138 if member_type == 'user':
139 r2p = RepoToPerm()
139 r2p = RepoToPerm()
140 r2p.repository = cur_repo
140 r2p.repository = cur_repo
141 r2p.user = user_model.get_by_username(member)
141 r2p.user = user_model.get_by_username(member)
142
142
143 r2p.permission = self.sa.query(Permission)\
143 r2p.permission = self.sa.query(Permission)\
144 .filter(Permission.permission_name == perm)\
144 .filter(Permission.permission_name == perm)\
145 .scalar()
145 .scalar()
146 self.sa.add(r2p)
146 self.sa.add(r2p)
147 else:
147 else:
148 g2p = UsersGroupToPerm()
148 g2p = UsersGroupToPerm()
149 g2p.repository = cur_repo
149 g2p.repository = cur_repo
150 g2p.users_group = users_group_model.get_by_groupname(member)
150 g2p.users_group = users_group_model.get_by_groupname(member)
151
151
152 g2p.permission = self.sa.query(Permission)\
152 g2p.permission = self.sa.query(Permission)\
153 .filter(Permission.permission_name == perm)\
153 .filter(Permission.permission_name == perm)\
154 .scalar()
154 .scalar()
155 self.sa.add(g2p)
155 self.sa.add(g2p)
156
156
157 #update current repo
157 #update current repo
158 for k, v in form_data.items():
158 for k, v in form_data.items():
159 if k == 'user':
159 if k == 'user':
160 cur_repo.user = user_model.get(v)
160 cur_repo.user = user_model.get(v)
161 else:
161 else:
162 setattr(cur_repo, k, v)
162 setattr(cur_repo, k, v)
163
163
164 self.sa.add(cur_repo)
164 self.sa.add(cur_repo)
165
165
166 if repo_name != form_data['repo_name']:
166 if repo_name != form_data['repo_name']:
167 #rename our data
167 #rename our data
168 self.__rename_repo(repo_name, form_data['repo_name'])
168 self.__rename_repo(repo_name, form_data['repo_name'])
169
169
170 self.sa.commit()
170 self.sa.commit()
171 except:
171 except:
172 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
173 self.sa.rollback()
173 self.sa.rollback()
174 raise
174 raise
175
175
176 def create(self, form_data, cur_user, just_db=False, fork=False):
176 def create(self, form_data, cur_user, just_db=False, fork=False):
177 try:
177 try:
178 if fork:
178 if fork:
179 #force str since hg doesn't go with unicode
179 #force str since hg doesn't go with unicode
180 repo_name = str(form_data['fork_name'])
180 repo_name = str(form_data['fork_name'])
181 org_name = str(form_data['repo_name'])
181 org_name = str(form_data['repo_name'])
182
182
183 else:
183 else:
184 org_name = repo_name = str(form_data['repo_name'])
184 org_name = repo_name = str(form_data['repo_name'])
185 new_repo = Repository()
185 new_repo = Repository()
186 new_repo.enable_statistics = True
186 new_repo.enable_statistics = True
187 for k, v in form_data.items():
187 for k, v in form_data.items():
188 if k == 'repo_name':
188 if k == 'repo_name':
189 v = repo_name
189 v = repo_name
190 setattr(new_repo, k, v)
190 setattr(new_repo, k, v)
191
191
192 if fork:
192 if fork:
193 parent_repo = self.sa.query(Repository)\
193 parent_repo = self.sa.query(Repository)\
194 .filter(Repository.repo_name == org_name).scalar()
194 .filter(Repository.repo_name == org_name).scalar()
195 new_repo.fork = parent_repo
195 new_repo.fork = parent_repo
196
196
197 new_repo.user_id = cur_user.user_id
197 new_repo.user_id = cur_user.user_id
198 self.sa.add(new_repo)
198 self.sa.add(new_repo)
199
199
200 #create default permission
200 #create default permission
201 repo_to_perm = RepoToPerm()
201 repo_to_perm = RepoToPerm()
202 default = 'repository.read'
202 default = 'repository.read'
203 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
203 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
204 if p.permission.permission_name.startswith('repository.'):
204 if p.permission.permission_name.startswith('repository.'):
205 default = p.permission.permission_name
205 default = p.permission.permission_name
206 break
206 break
207
207
208 default_perm = 'repository.none' if form_data['private'] else default
208 default_perm = 'repository.none' if form_data['private'] else default
209
209
210 repo_to_perm.permission_id = self.sa.query(Permission)\
210 repo_to_perm.permission_id = self.sa.query(Permission)\
211 .filter(Permission.permission_name == default_perm)\
211 .filter(Permission.permission_name == default_perm)\
212 .one().permission_id
212 .one().permission_id
213
213
214 repo_to_perm.repository_id = new_repo.repo_id
214 repo_to_perm.repository = new_repo
215 repo_to_perm.user_id = UserModel(self.sa)\
215 repo_to_perm.user_id = UserModel(self.sa)\
216 .get_by_username('default', cache=False).user_id
216 .get_by_username('default', cache=False).user_id
217
217
218 self.sa.add(repo_to_perm)
218 self.sa.add(repo_to_perm)
219
220 if not just_db:
221 self.__create_repo(repo_name, form_data['repo_type'])
222
219 self.sa.commit()
223 self.sa.commit()
220
224
221
222 #now automatically start following this repository as owner
225 #now automatically start following this repository as owner
223 from rhodecode.model.scm import ScmModel
226 from rhodecode.model.scm import ScmModel
224 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
227 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
225 cur_user.user_id)
228 cur_user.user_id)
226
229
227 if not just_db:
228 self.__create_repo(repo_name, form_data['repo_type'])
229 except:
230 except:
230 log.error(traceback.format_exc())
231 log.error(traceback.format_exc())
231 self.sa.rollback()
232 self.sa.rollback()
232 raise
233 raise
233
234
234 def create_fork(self, form_data, cur_user):
235 def create_fork(self, form_data, cur_user):
235 from rhodecode.lib.celerylib import tasks, run_task
236 from rhodecode.lib.celerylib import tasks, run_task
236 run_task(tasks.create_repo_fork, form_data, cur_user)
237 run_task(tasks.create_repo_fork, form_data, cur_user)
237
238
238 def delete(self, repo):
239 def delete(self, repo):
239 try:
240 try:
240 self.sa.delete(repo)
241 self.sa.delete(repo)
241 self.__delete_repo(repo)
242 self.__delete_repo(repo)
242 self.sa.commit()
243 self.sa.commit()
243 except:
244 except:
244 log.error(traceback.format_exc())
245 log.error(traceback.format_exc())
245 self.sa.rollback()
246 self.sa.rollback()
246 raise
247 raise
247
248
248 def delete_perm_user(self, form_data, repo_name):
249 def delete_perm_user(self, form_data, repo_name):
249 try:
250 try:
250 self.sa.query(RepoToPerm)\
251 self.sa.query(RepoToPerm)\
251 .filter(RepoToPerm.repository \
252 .filter(RepoToPerm.repository \
252 == self.get_by_repo_name(repo_name))\
253 == self.get_by_repo_name(repo_name))\
253 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
254 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
254 self.sa.commit()
255 self.sa.commit()
255 except:
256 except:
256 log.error(traceback.format_exc())
257 log.error(traceback.format_exc())
257 self.sa.rollback()
258 self.sa.rollback()
258 raise
259 raise
259
260
260 def delete_perm_users_group(self, form_data, repo_name):
261 def delete_perm_users_group(self, form_data, repo_name):
261 try:
262 try:
262 self.sa.query(UsersGroupToPerm)\
263 self.sa.query(UsersGroupToPerm)\
263 .filter(UsersGroupToPerm.repository \
264 .filter(UsersGroupToPerm.repository \
264 == self.get_by_repo_name(repo_name))\
265 == self.get_by_repo_name(repo_name))\
265 .filter(UsersGroupToPerm.users_group_id \
266 .filter(UsersGroupToPerm.users_group_id \
266 == form_data['users_group_id']).delete()
267 == form_data['users_group_id']).delete()
267 self.sa.commit()
268 self.sa.commit()
268 except:
269 except:
269 log.error(traceback.format_exc())
270 log.error(traceback.format_exc())
270 self.sa.rollback()
271 self.sa.rollback()
271 raise
272 raise
272
273
273 def delete_stats(self, repo_name):
274 def delete_stats(self, repo_name):
274 try:
275 try:
275 self.sa.query(Statistics)\
276 self.sa.query(Statistics)\
276 .filter(Statistics.repository == \
277 .filter(Statistics.repository == \
277 self.get_by_repo_name(repo_name)).delete()
278 self.get_by_repo_name(repo_name)).delete()
278 self.sa.commit()
279 self.sa.commit()
279 except:
280 except:
280 log.error(traceback.format_exc())
281 log.error(traceback.format_exc())
281 self.sa.rollback()
282 self.sa.rollback()
282 raise
283 raise
283
284
284
285
285 def __create_repo(self, repo_name, alias):
286 def __create_repo(self, repo_name, alias):
286 """
287 """
287 makes repository on filesystem
288 makes repository on filesystem
288 :param repo_name:
289 :param repo_name:
289 :param alias:
290 :param alias:
290 """
291 """
291 from rhodecode.lib.utils import check_repo
292 from rhodecode.lib.utils import check_repo
292 repo_path = os.path.join(self.base_path, repo_name)
293 repo_path = os.path.join(self.base_path, repo_name)
293 if check_repo(repo_name, self.base_path):
294 if check_repo(repo_name, self.base_path):
294 log.info('creating repo %s in %s', repo_name, repo_path)
295 log.info('creating repo %s in %s', repo_name, repo_path)
295 backend = get_backend(alias)
296 backend = get_backend(alias)
296 backend(repo_path, create=True)
297 backend(repo_path, create=True)
297
298
298 def __rename_repo(self, old, new):
299 def __rename_repo(self, old, new):
299 """
300 """
300 renames repository on filesystem
301 renames repository on filesystem
301 :param old: old name
302 :param old: old name
302 :param new: new name
303 :param new: new name
303 """
304 """
304 log.info('renaming repo from %s to %s', old, new)
305 log.info('renaming repo from %s to %s', old, new)
305
306
306 old_path = os.path.join(self.base_path, old)
307 old_path = os.path.join(self.base_path, old)
307 new_path = os.path.join(self.base_path, new)
308 new_path = os.path.join(self.base_path, new)
308 if os.path.isdir(new_path):
309 if os.path.isdir(new_path):
309 raise Exception('Was trying to rename to already existing dir %s',
310 raise Exception('Was trying to rename to already existing dir %s',
310 new_path)
311 new_path)
311 shutil.move(old_path, new_path)
312 shutil.move(old_path, new_path)
312
313
313 def __delete_repo(self, repo):
314 def __delete_repo(self, repo):
314 """
315 """
315 removes repo from filesystem, the removal is acctually made by
316 removes repo from filesystem, the removal is acctually made by
316 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
317 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
317 repository is no longer valid for rhodecode, can be undeleted later on
318 repository is no longer valid for rhodecode, can be undeleted later on
318 by reverting the renames on this repository
319 by reverting the renames on this repository
319 :param repo: repo object
320 :param repo: repo object
320 """
321 """
321 rm_path = os.path.join(self.base_path, repo.repo_name)
322 rm_path = os.path.join(self.base_path, repo.repo_name)
322 log.info("Removing %s", rm_path)
323 log.info("Removing %s", rm_path)
323 #disable hg/git
324 #disable hg/git
324 alias = repo.repo_type
325 alias = repo.repo_type
325 shutil.move(os.path.join(rm_path, '.%s' % alias),
326 shutil.move(os.path.join(rm_path, '.%s' % alias),
326 os.path.join(rm_path, 'rm__.%s' % alias))
327 os.path.join(rm_path, 'rm__.%s' % alias))
327 #disable repo
328 #disable repo
328 shutil.move(rm_path, os.path.join(self.base_path, 'rm__%s__%s' \
329 shutil.move(rm_path, os.path.join(self.base_path, 'rm__%s__%s' \
329 % (datetime.today().isoformat(),
330 % (datetime.today().isoformat(),
330 repo.repo_name)))
331 repo.repo_name)))
@@ -1,384 +1,386 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31
31
32 from mercurial import ui
32 from mercurial import ui
33
33
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
35 from sqlalchemy.orm.session import make_transient
35 from sqlalchemy.orm.session import make_transient
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37
37
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39
39
40 from vcs import get_backend
40 from vcs import get_backend
41 from vcs.utils.helpers import get_scm
41 from vcs.utils.helpers import get_scm
42 from vcs.exceptions import RepositoryError, VCSError
42 from vcs.exceptions import RepositoryError, VCSError
43 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.lazy import LazyProperty
44
44
45 from rhodecode import BACKENDS
45 from rhodecode import BACKENDS
46 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
47 from rhodecode.lib.auth import HasRepoPermissionAny
47 from rhodecode.lib.auth import HasRepoPermissionAny
48 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
48 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
49 from rhodecode.model import BaseModel
49 from rhodecode.model import BaseModel
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
52 UserFollowing, UserLog
52 UserFollowing, UserLog
53 from rhodecode.model.caching_query import FromCache
53 from rhodecode.model.caching_query import FromCache
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class UserTemp(object):
58 class UserTemp(object):
59 def __init__(self, user_id):
59 def __init__(self, user_id):
60 self.user_id = user_id
60 self.user_id = user_id
61
61
62 def __repr__(self):
62 def __repr__(self):
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64
64
65 class RepoTemp(object):
65 class RepoTemp(object):
66 def __init__(self, repo_id):
66 def __init__(self, repo_id):
67 self.repo_id = repo_id
67 self.repo_id = repo_id
68
68
69 def __repr__(self):
69 def __repr__(self):
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
71
71
72 class ScmModel(BaseModel):
72 class ScmModel(BaseModel):
73 """Generic Scm Model
73 """Generic Scm Model
74 """
74 """
75
75
76 @LazyProperty
76 @LazyProperty
77 def repos_path(self):
77 def repos_path(self):
78 """Get's the repositories root path from database
78 """Get's the repositories root path from database
79 """
79 """
80
80
81 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
81 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
82
82
83 return q.ui_value
83 return q.ui_value
84
84
85 def repo_scan(self, repos_path, baseui):
85 def repo_scan(self, repos_path, baseui):
86 """Listing of repositories in given path. This path should not be a
86 """Listing of repositories in given path. This path should not be a
87 repository itself. Return a dictionary of repository objects
87 repository itself. Return a dictionary of repository objects
88
88
89 :param repos_path: path to directory containing repositories
89 :param repos_path: path to directory containing repositories
90 :param baseui: baseui instance to instantiate MercurialRepostitory with
90 :param baseui: baseui instance to instantiate MercurialRepostitory with
91 """
91 """
92
92
93 log.info('scanning for repositories in %s', repos_path)
93 log.info('scanning for repositories in %s', repos_path)
94
94
95 if not isinstance(baseui, ui.ui):
95 if not isinstance(baseui, ui.ui):
96 baseui = make_ui('db')
96 baseui = make_ui('db')
97 repos_list = {}
97 repos_list = {}
98
98
99 for name, path in get_filesystem_repos(repos_path, recursive=True):
99 for name, path in get_filesystem_repos(repos_path, recursive=True):
100 try:
100 try:
101 if repos_list.has_key(name):
101 if repos_list.has_key(name):
102 raise RepositoryError('Duplicate repository name %s '
102 raise RepositoryError('Duplicate repository name %s '
103 'found in %s' % (name, path))
103 'found in %s' % (name, path))
104 else:
104 else:
105
105
106 klass = get_backend(path[0])
106 klass = get_backend(path[0])
107
107
108 if path[0] == 'hg' and path[0] in BACKENDS.keys():
108 if path[0] == 'hg' and path[0] in BACKENDS.keys():
109 repos_list[name] = klass(path[1], baseui=baseui)
109 repos_list[name] = klass(path[1], baseui=baseui)
110
110
111 if path[0] == 'git' and path[0] in BACKENDS.keys():
111 if path[0] == 'git' and path[0] in BACKENDS.keys():
112 repos_list[name] = klass(path[1])
112 repos_list[name] = klass(path[1])
113 except OSError:
113 except OSError:
114 continue
114 continue
115
115
116 return repos_list
116 return repos_list
117
117
118 def get_repos(self, all_repos=None):
118 def get_repos(self, all_repos=None):
119 """Get all repos from db and for each repo create it's backend instance.
119 """Get all repos from db and for each repo create it's backend instance.
120 and fill that backed with information from database
120 and fill that backed with information from database
121
121
122 :param all_repos: give specific repositories list, good for filtering
122 :param all_repos: give specific repositories list, good for filtering
123 """
123 """
124
124
125 if all_repos is None:
125 if all_repos is None:
126 all_repos = self.sa.query(Repository)\
126 all_repos = self.sa.query(Repository)\
127 .order_by(Repository.repo_name).all()
127 .order_by(Repository.repo_name).all()
128
128
129 #get the repositories that should be invalidated
129 #get the repositories that should be invalidated
130 invalidation_list = [str(x.cache_key) for x in \
130 invalidation_list = [str(x.cache_key) for x in \
131 self.sa.query(CacheInvalidation.cache_key)\
131 self.sa.query(CacheInvalidation.cache_key)\
132 .filter(CacheInvalidation.cache_active == False)\
132 .filter(CacheInvalidation.cache_active == False)\
133 .all()]
133 .all()]
134
134
135 for r in all_repos:
135 for r in all_repos:
136
136
137 repo = self.get(r.repo_name, invalidation_list)
137 repo = self.get(r.repo_name, invalidation_list)
138
138
139 if repo is not None:
139 if repo is not None:
140 last_change = repo.last_change
140 last_change = repo.last_change
141 tip = h.get_changeset_safe(repo, 'tip')
141 tip = h.get_changeset_safe(repo, 'tip')
142
142
143 tmp_d = {}
143 tmp_d = {}
144 tmp_d['name'] = r.repo_name
144 tmp_d['name'] = r.repo_name
145 tmp_d['name_sort'] = tmp_d['name'].lower()
145 tmp_d['name_sort'] = tmp_d['name'].lower()
146 tmp_d['description'] = repo.dbrepo.description
146 tmp_d['description'] = repo.dbrepo.description
147 tmp_d['description_sort'] = tmp_d['description']
147 tmp_d['description_sort'] = tmp_d['description']
148 tmp_d['last_change'] = last_change
148 tmp_d['last_change'] = last_change
149 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
149 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
150 tmp_d['tip'] = tip.raw_id
150 tmp_d['tip'] = tip.raw_id
151 tmp_d['tip_sort'] = tip.revision
151 tmp_d['tip_sort'] = tip.revision
152 tmp_d['rev'] = tip.revision
152 tmp_d['rev'] = tip.revision
153 tmp_d['contact'] = repo.dbrepo.user.full_contact
153 tmp_d['contact'] = repo.dbrepo.user.full_contact
154 tmp_d['contact_sort'] = tmp_d['contact']
154 tmp_d['contact_sort'] = tmp_d['contact']
155 tmp_d['owner_sort'] = tmp_d['contact']
155 tmp_d['owner_sort'] = tmp_d['contact']
156 tmp_d['repo_archives'] = list(repo._get_archives())
156 tmp_d['repo_archives'] = list(repo._get_archives())
157 tmp_d['last_msg'] = tip.message
157 tmp_d['last_msg'] = tip.message
158 tmp_d['repo'] = repo
158 tmp_d['repo'] = repo
159 yield tmp_d
159 yield tmp_d
160
160
161 def get_repo(self, repo_name):
161 def get_repo(self, repo_name):
162 return self.get(repo_name)
162 return self.get(repo_name)
163
163
164 def get(self, repo_name, invalidation_list=None):
164 def get(self, repo_name, invalidation_list=None):
165 """Get's repository from given name, creates BackendInstance and
165 """Get's repository from given name, creates BackendInstance and
166 propagates it's data from database with all additional information
166 propagates it's data from database with all additional information
167
167
168 :param repo_name:
168 :param repo_name:
169 :param invalidation_list: if a invalidation list is given the get
169 :param invalidation_list: if a invalidation list is given the get
170 method should not manually check if this repository needs
170 method should not manually check if this repository needs
171 invalidation and just invalidate the repositories in list
171 invalidation and just invalidate the repositories in list
172
172
173 """
173 """
174 if not HasRepoPermissionAny('repository.read', 'repository.write',
174 if not HasRepoPermissionAny('repository.read', 'repository.write',
175 'repository.admin')(repo_name, 'get repo check'):
175 'repository.admin')(repo_name, 'get repo check'):
176 return
176 return
177
177
178 #======================================================================
178 #======================================================================
179 # CACHE FUNCTION
179 # CACHE FUNCTION
180 #======================================================================
180 #======================================================================
181 @cache_region('long_term')
181 @cache_region('long_term')
182 def _get_repo(repo_name):
182 def _get_repo(repo_name):
183
183
184 repo_path = os.path.join(self.repos_path, repo_name)
184 repo_path = os.path.join(self.repos_path, repo_name)
185
185
186 try:
186 try:
187 alias = get_scm(repo_path)[0]
187 alias = get_scm(repo_path)[0]
188
189 log.debug('Creating instance of %s repository', alias)
188 log.debug('Creating instance of %s repository', alias)
190 backend = get_backend(alias)
189 backend = get_backend(alias)
191 except VCSError:
190 except VCSError:
192 log.error(traceback.format_exc())
191 log.error(traceback.format_exc())
192 log.error('Perhaps this repository is in db and not in filesystem'
193 'run rescan repositories with "destroy old data "'
194 'option from admin panel')
193 return
195 return
194
196
195 if alias == 'hg':
197 if alias == 'hg':
196 from pylons import app_globals as g
198 from pylons import app_globals as g
197 repo = backend(repo_path, create=False, baseui=g.baseui)
199 repo = backend(repo_path, create=False, baseui=g.baseui)
198 #skip hidden web repository
200 #skip hidden web repository
199 if repo._get_hidden():
201 if repo._get_hidden():
200 return
202 return
201 else:
203 else:
202 repo = backend(repo_path, create=False)
204 repo = backend(repo_path, create=False)
203
205
204 dbrepo = self.sa.query(Repository)\
206 dbrepo = self.sa.query(Repository)\
205 .options(joinedload(Repository.fork))\
207 .options(joinedload(Repository.fork))\
206 .options(joinedload(Repository.user))\
208 .options(joinedload(Repository.user))\
207 .filter(Repository.repo_name == repo_name)\
209 .filter(Repository.repo_name == repo_name)\
208 .scalar()
210 .scalar()
209
211
210 make_transient(dbrepo)
212 make_transient(dbrepo)
211 if dbrepo.user:
213 if dbrepo.user:
212 make_transient(dbrepo.user)
214 make_transient(dbrepo.user)
213 if dbrepo.fork:
215 if dbrepo.fork:
214 make_transient(dbrepo.fork)
216 make_transient(dbrepo.fork)
215
217
216 repo.dbrepo = dbrepo
218 repo.dbrepo = dbrepo
217 return repo
219 return repo
218
220
219 pre_invalidate = True
221 pre_invalidate = True
220 if invalidation_list is not None:
222 if invalidation_list is not None:
221 pre_invalidate = repo_name in invalidation_list
223 pre_invalidate = repo_name in invalidation_list
222
224
223 if pre_invalidate:
225 if pre_invalidate:
224 invalidate = self._should_invalidate(repo_name)
226 invalidate = self._should_invalidate(repo_name)
225
227
226 if invalidate:
228 if invalidate:
227 log.info('invalidating cache for repository %s', repo_name)
229 log.info('invalidating cache for repository %s', repo_name)
228 region_invalidate(_get_repo, None, repo_name)
230 region_invalidate(_get_repo, None, repo_name)
229 self._mark_invalidated(invalidate)
231 self._mark_invalidated(invalidate)
230
232
231 return _get_repo(repo_name)
233 return _get_repo(repo_name)
232
234
233
235
234
236
235 def mark_for_invalidation(self, repo_name):
237 def mark_for_invalidation(self, repo_name):
236 """Puts cache invalidation task into db for
238 """Puts cache invalidation task into db for
237 further global cache invalidation
239 further global cache invalidation
238
240
239 :param repo_name: this repo that should invalidation take place
241 :param repo_name: this repo that should invalidation take place
240 """
242 """
241
243
242 log.debug('marking %s for invalidation', repo_name)
244 log.debug('marking %s for invalidation', repo_name)
243 cache = self.sa.query(CacheInvalidation)\
245 cache = self.sa.query(CacheInvalidation)\
244 .filter(CacheInvalidation.cache_key == repo_name).scalar()
246 .filter(CacheInvalidation.cache_key == repo_name).scalar()
245
247
246 if cache:
248 if cache:
247 #mark this cache as inactive
249 #mark this cache as inactive
248 cache.cache_active = False
250 cache.cache_active = False
249 else:
251 else:
250 log.debug('cache key not found in invalidation db -> creating one')
252 log.debug('cache key not found in invalidation db -> creating one')
251 cache = CacheInvalidation(repo_name)
253 cache = CacheInvalidation(repo_name)
252
254
253 try:
255 try:
254 self.sa.add(cache)
256 self.sa.add(cache)
255 self.sa.commit()
257 self.sa.commit()
256 except (DatabaseError,):
258 except (DatabaseError,):
257 log.error(traceback.format_exc())
259 log.error(traceback.format_exc())
258 self.sa.rollback()
260 self.sa.rollback()
259
261
260
262
261 def toggle_following_repo(self, follow_repo_id, user_id):
263 def toggle_following_repo(self, follow_repo_id, user_id):
262
264
263 f = self.sa.query(UserFollowing)\
265 f = self.sa.query(UserFollowing)\
264 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
266 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
265 .filter(UserFollowing.user_id == user_id).scalar()
267 .filter(UserFollowing.user_id == user_id).scalar()
266
268
267 if f is not None:
269 if f is not None:
268
270
269 try:
271 try:
270 self.sa.delete(f)
272 self.sa.delete(f)
271 self.sa.commit()
273 self.sa.commit()
272 action_logger(UserTemp(user_id),
274 action_logger(UserTemp(user_id),
273 'stopped_following_repo',
275 'stopped_following_repo',
274 RepoTemp(follow_repo_id))
276 RepoTemp(follow_repo_id))
275 return
277 return
276 except:
278 except:
277 log.error(traceback.format_exc())
279 log.error(traceback.format_exc())
278 self.sa.rollback()
280 self.sa.rollback()
279 raise
281 raise
280
282
281
283
282 try:
284 try:
283 f = UserFollowing()
285 f = UserFollowing()
284 f.user_id = user_id
286 f.user_id = user_id
285 f.follows_repo_id = follow_repo_id
287 f.follows_repo_id = follow_repo_id
286 self.sa.add(f)
288 self.sa.add(f)
287 self.sa.commit()
289 self.sa.commit()
288 action_logger(UserTemp(user_id),
290 action_logger(UserTemp(user_id),
289 'started_following_repo',
291 'started_following_repo',
290 RepoTemp(follow_repo_id))
292 RepoTemp(follow_repo_id))
291 except:
293 except:
292 log.error(traceback.format_exc())
294 log.error(traceback.format_exc())
293 self.sa.rollback()
295 self.sa.rollback()
294 raise
296 raise
295
297
296 def toggle_following_user(self, follow_user_id , user_id):
298 def toggle_following_user(self, follow_user_id , user_id):
297 f = self.sa.query(UserFollowing)\
299 f = self.sa.query(UserFollowing)\
298 .filter(UserFollowing.follows_user_id == follow_user_id)\
300 .filter(UserFollowing.follows_user_id == follow_user_id)\
299 .filter(UserFollowing.user_id == user_id).scalar()
301 .filter(UserFollowing.user_id == user_id).scalar()
300
302
301 if f is not None:
303 if f is not None:
302 try:
304 try:
303 self.sa.delete(f)
305 self.sa.delete(f)
304 self.sa.commit()
306 self.sa.commit()
305 return
307 return
306 except:
308 except:
307 log.error(traceback.format_exc())
309 log.error(traceback.format_exc())
308 self.sa.rollback()
310 self.sa.rollback()
309 raise
311 raise
310
312
311 try:
313 try:
312 f = UserFollowing()
314 f = UserFollowing()
313 f.user_id = user_id
315 f.user_id = user_id
314 f.follows_user_id = follow_user_id
316 f.follows_user_id = follow_user_id
315 self.sa.add(f)
317 self.sa.add(f)
316 self.sa.commit()
318 self.sa.commit()
317 except:
319 except:
318 log.error(traceback.format_exc())
320 log.error(traceback.format_exc())
319 self.sa.rollback()
321 self.sa.rollback()
320 raise
322 raise
321
323
322 def is_following_repo(self, repo_name, user_id, cache=False):
324 def is_following_repo(self, repo_name, user_id, cache=False):
323 r = self.sa.query(Repository)\
325 r = self.sa.query(Repository)\
324 .filter(Repository.repo_name == repo_name).scalar()
326 .filter(Repository.repo_name == repo_name).scalar()
325
327
326 f = self.sa.query(UserFollowing)\
328 f = self.sa.query(UserFollowing)\
327 .filter(UserFollowing.follows_repository == r)\
329 .filter(UserFollowing.follows_repository == r)\
328 .filter(UserFollowing.user_id == user_id).scalar()
330 .filter(UserFollowing.user_id == user_id).scalar()
329
331
330 return f is not None
332 return f is not None
331
333
332 def is_following_user(self, username, user_id, cache=False):
334 def is_following_user(self, username, user_id, cache=False):
333 u = UserModel(self.sa).get_by_username(username)
335 u = UserModel(self.sa).get_by_username(username)
334
336
335 f = self.sa.query(UserFollowing)\
337 f = self.sa.query(UserFollowing)\
336 .filter(UserFollowing.follows_user == u)\
338 .filter(UserFollowing.follows_user == u)\
337 .filter(UserFollowing.user_id == user_id).scalar()
339 .filter(UserFollowing.user_id == user_id).scalar()
338
340
339 return f is not None
341 return f is not None
340
342
341 def get_followers(self, repo_id):
343 def get_followers(self, repo_id):
342 return self.sa.query(UserFollowing)\
344 return self.sa.query(UserFollowing)\
343 .filter(UserFollowing.follows_repo_id == repo_id).count()
345 .filter(UserFollowing.follows_repo_id == repo_id).count()
344
346
345 def get_forks(self, repo_id):
347 def get_forks(self, repo_id):
346 return self.sa.query(Repository)\
348 return self.sa.query(Repository)\
347 .filter(Repository.fork_id == repo_id).count()
349 .filter(Repository.fork_id == repo_id).count()
348
350
349
351
350 def get_unread_journal(self):
352 def get_unread_journal(self):
351 return self.sa.query(UserLog).count()
353 return self.sa.query(UserLog).count()
352
354
353
355
354 def _should_invalidate(self, repo_name):
356 def _should_invalidate(self, repo_name):
355 """Looks up database for invalidation signals for this repo_name
357 """Looks up database for invalidation signals for this repo_name
356
358
357 :param repo_name:
359 :param repo_name:
358 """
360 """
359
361
360 ret = self.sa.query(CacheInvalidation)\
362 ret = self.sa.query(CacheInvalidation)\
361 .options(FromCache('sql_cache_short',
363 .options(FromCache('sql_cache_short',
362 'get_invalidation_%s' % repo_name))\
364 'get_invalidation_%s' % repo_name))\
363 .filter(CacheInvalidation.cache_key == repo_name)\
365 .filter(CacheInvalidation.cache_key == repo_name)\
364 .filter(CacheInvalidation.cache_active == False)\
366 .filter(CacheInvalidation.cache_active == False)\
365 .scalar()
367 .scalar()
366
368
367 return ret
369 return ret
368
370
369 def _mark_invalidated(self, cache_key):
371 def _mark_invalidated(self, cache_key):
370 """ Marks all occurences of cache to invaldation as already invalidated
372 """ Marks all occurences of cache to invaldation as already invalidated
371
373
372 :param cache_key:
374 :param cache_key:
373 """
375 """
374
376
375 if cache_key:
377 if cache_key:
376 log.debug('marking %s as already invalidated', cache_key)
378 log.debug('marking %s as already invalidated', cache_key)
377 try:
379 try:
378 cache_key.cache_active = True
380 cache_key.cache_active = True
379 self.sa.add(cache_key)
381 self.sa.add(cache_key)
380 self.sa.commit()
382 self.sa.commit()
381 except (DatabaseError,):
383 except (DatabaseError,):
382 log.error(traceback.format_exc())
384 log.error(traceback.format_exc())
383 self.sa.rollback()
385 self.sa.rollback()
384
386
General Comments 0
You need to be logged in to leave comments. Login now