##// END OF EJS Templates
gists: fix problem of deleted gists repositories.
marcink -
r2817:ae0b3477 default
parent child Browse files
Show More
@@ -1,250 +1,255 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-2018 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 gist model for RhodeCode
23 23 """
24 24
25 25 import os
26 26 import time
27 27 import logging
28 28 import traceback
29 29 import shutil
30 30
31 31 from pyramid.threadlocal import get_current_request
32 32
33 33 from rhodecode.lib.utils2 import (
34 34 safe_unicode, unique_id, safe_int, time_to_datetime, AttributeDict)
35 35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.vcs import VCSError
36 37 from rhodecode.model import BaseModel
37 38 from rhodecode.model.db import Gist
38 39 from rhodecode.model.repo import RepoModel
39 40 from rhodecode.model.scm import ScmModel
40 41
41 42 log = logging.getLogger(__name__)
42 43
43 44 GIST_STORE_LOC = '.rc_gist_store'
44 45 GIST_METADATA_FILE = '.rc_gist_metadata'
45 46
46 47
47 48 class GistModel(BaseModel):
48 49 cls = Gist
49 50
50 51 def _get_gist(self, gist):
51 52 """
52 53 Helper method to get gist by ID, or gist_access_id as a fallback
53 54
54 55 :param gist: GistID, gist_access_id, or Gist instance
55 56 """
56 57 return self._get_instance(Gist, gist, callback=Gist.get_by_access_id)
57 58
58 59 def __delete_gist(self, gist):
59 60 """
60 61 removes gist from filesystem
61 62
62 63 :param gist: gist object
63 64 """
64 65 root_path = RepoModel().repos_path
65 66 rm_path = os.path.join(root_path, GIST_STORE_LOC, gist.gist_access_id)
66 67 log.info("Removing %s", rm_path)
67 68 shutil.rmtree(rm_path)
68 69
69 70 def _store_metadata(self, repo, gist_id, gist_access_id, user_id, username,
70 71 gist_type, gist_expires, gist_acl_level):
71 72 """
72 73 store metadata inside the gist repo, this can be later used for imports
73 74 or gist identification. Currently we use this inside RhodeCode tools
74 75 to do cleanup of gists that are in storage but not in database.
75 76 """
76 77 metadata = {
77 78 'metadata_version': '2',
78 79 'gist_db_id': gist_id,
79 80 'gist_access_id': gist_access_id,
80 81 'gist_owner_id': user_id,
81 82 'gist_owner_username': username,
82 83 'gist_type': gist_type,
83 84 'gist_expires': gist_expires,
84 85 'gist_updated': time.time(),
85 86 'gist_acl_level': gist_acl_level,
86 87 }
87 88 metadata_file = os.path.join(repo.path, '.hg', GIST_METADATA_FILE)
88 89 with open(metadata_file, 'wb') as f:
89 90 f.write(json.dumps(metadata))
90 91
91 92 def get_gist(self, gist):
92 93 return self._get_gist(gist)
93 94
94 95 def get_gist_files(self, gist_access_id, revision=None):
95 96 """
96 97 Get files for given gist
97 98
98 99 :param gist_access_id:
99 100 """
100 101 repo = Gist.get_by_access_id(gist_access_id)
101 commit = repo.scm_instance().get_commit(commit_id=revision)
102 vcs_repo = repo.scm_instance()
103 if not vcs_repo:
104 raise VCSError('Failed to load gist repository for {}'.format(repo))
105
106 commit = vcs_repo.get_commit(commit_id=revision)
102 107 return commit, [n for n in commit.get_node('/')]
103 108
104 109 def create(self, description, owner, gist_mapping,
105 110 gist_type=Gist.GIST_PUBLIC, lifetime=-1, gist_id=None,
106 111 gist_acl_level=Gist.ACL_LEVEL_PRIVATE):
107 112 """
108 113 Create a gist
109 114
110 115 :param description: description of the gist
111 116 :param owner: user who created this gist
112 117 :param gist_mapping: mapping [{'filename': 'file1.txt', 'content': content}, ...}]
113 118 :param gist_type: type of gist private/public
114 119 :param lifetime: in minutes, -1 == forever
115 120 :param gist_acl_level: acl level for this gist
116 121 """
117 122 owner = self._get_user(owner)
118 123 gist_id = safe_unicode(gist_id or unique_id(20))
119 124 lifetime = safe_int(lifetime, -1)
120 125 gist_expires = time.time() + (lifetime * 60) if lifetime != -1 else -1
121 126 expiration = (time_to_datetime(gist_expires)
122 127 if gist_expires != -1 else 'forever')
123 128 log.debug('set GIST expiration date to: %s', expiration)
124 129 # create the Database version
125 130 gist = Gist()
126 131 gist.gist_description = description
127 132 gist.gist_access_id = gist_id
128 133 gist.gist_owner = owner.user_id
129 134 gist.gist_expires = gist_expires
130 135 gist.gist_type = safe_unicode(gist_type)
131 136 gist.acl_level = gist_acl_level
132 137 self.sa.add(gist)
133 138 self.sa.flush()
134 139 if gist_type == Gist.GIST_PUBLIC:
135 140 # use DB ID for easy to use GIST ID
136 141 gist_id = safe_unicode(gist.gist_id)
137 142 gist.gist_access_id = gist_id
138 143 self.sa.add(gist)
139 144
140 145 gist_repo_path = os.path.join(GIST_STORE_LOC, gist_id)
141 146 log.debug('Creating new %s GIST repo in %s', gist_type, gist_repo_path)
142 147 repo = RepoModel()._create_filesystem_repo(
143 148 repo_name=gist_id, repo_type='hg', repo_group=GIST_STORE_LOC,
144 149 use_global_config=True)
145 150
146 151 # now create single multifile commit
147 152 message = 'added file'
148 153 message += 's: ' if len(gist_mapping) > 1 else ': '
149 154 message += ', '.join([x for x in gist_mapping])
150 155
151 156 # fake RhodeCode Repository object
152 157 fake_repo = AttributeDict({
153 158 'repo_name': gist_repo_path,
154 159 'scm_instance': lambda *args, **kwargs: repo,
155 160 })
156 161
157 162 ScmModel().create_nodes(
158 163 user=owner.user_id, repo=fake_repo,
159 164 message=message,
160 165 nodes=gist_mapping,
161 166 trigger_push_hook=False
162 167 )
163 168
164 169 self._store_metadata(repo, gist.gist_id, gist.gist_access_id,
165 170 owner.user_id, owner.username, gist.gist_type,
166 171 gist.gist_expires, gist_acl_level)
167 172 return gist
168 173
169 174 def delete(self, gist, fs_remove=True):
170 175 gist = self._get_gist(gist)
171 176 try:
172 177 self.sa.delete(gist)
173 178 if fs_remove:
174 179 self.__delete_gist(gist)
175 180 else:
176 181 log.debug('skipping removal from filesystem')
177 182 except Exception:
178 183 log.error(traceback.format_exc())
179 184 raise
180 185
181 186 def update(self, gist, description, owner, gist_mapping, lifetime,
182 187 gist_acl_level):
183 188 gist = self._get_gist(gist)
184 189 gist_repo = gist.scm_instance()
185 190
186 191 if lifetime == 0: # preserve old value
187 192 gist_expires = gist.gist_expires
188 193 else:
189 194 gist_expires = (
190 195 time.time() + (lifetime * 60) if lifetime != -1 else -1)
191 196
192 197 # calculate operation type based on given data
193 198 gist_mapping_op = {}
194 199 for k, v in gist_mapping.items():
195 200 # add, mod, del
196 201 if not v['filename_org'] and v['filename']:
197 202 op = 'add'
198 203 elif v['filename_org'] and not v['filename']:
199 204 op = 'del'
200 205 else:
201 206 op = 'mod'
202 207
203 208 v['op'] = op
204 209 gist_mapping_op[k] = v
205 210
206 211 gist.gist_description = description
207 212 gist.gist_expires = gist_expires
208 213 gist.owner = owner
209 214 gist.acl_level = gist_acl_level
210 215 self.sa.add(gist)
211 216 self.sa.flush()
212 217
213 218 message = 'updated file'
214 219 message += 's: ' if len(gist_mapping) > 1 else ': '
215 220 message += ', '.join([x for x in gist_mapping])
216 221
217 222 # fake RhodeCode Repository object
218 223 fake_repo = AttributeDict({
219 224 'repo_name': gist_repo.path,
220 225 'scm_instance': lambda *args, **kwargs: gist_repo,
221 226 })
222 227
223 228 self._store_metadata(gist_repo, gist.gist_id, gist.gist_access_id,
224 229 owner.user_id, owner.username, gist.gist_type,
225 230 gist.gist_expires, gist_acl_level)
226 231
227 232 # this can throw NodeNotChangedError, if changes we're trying to commit
228 233 # are not actually changes...
229 234 ScmModel().update_nodes(
230 235 user=owner.user_id,
231 236 repo=fake_repo,
232 237 message=message,
233 238 nodes=gist_mapping_op,
234 239 trigger_push_hook=False
235 240 )
236 241
237 242 return gist
238 243
239 244 def get_url(self, gist, request=None):
240 245 import rhodecode
241 246
242 247 if not request:
243 248 request = get_current_request()
244 249
245 250 alias_url = rhodecode.CONFIG.get('gist_alias_url')
246 251 if alias_url:
247 252 return alias_url.replace('{gistid}', gist.gist_access_id)
248 253
249 254 return request.route_url('gist_show', gist_id=gist.gist_access_id)
250 255
General Comments 0
You need to be logged in to leave comments. Login now