Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(626)

Side by Side Diff: appengine/chrome_infra_packages/cipd/impl.py

Issue 1194803002: Add a package listing API to cipd. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Implementation of package repository service. 5 """Implementation of package repository service.
6 6
7 Package and PackageInstance 7 Package and PackageInstance
8 --------------------------- 8 ---------------------------
9 9
10 Definitions: 10 Definitions:
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
76 ACL changes are applied atomically (e.g. multiple ACLs can be changed all at 76 ACL changes are applied atomically (e.g. multiple ACLs can be changed all at
77 once or none at all), but checks are still only eventually consistent (for 77 once or none at all), but checks are still only eventually consistent (for
78 performance and code simplicity). 78 performance and code simplicity).
79 79
80 TODO(vadimsh): Add fine grain ACL for tags. Tags that are set by Buildbot 80 TODO(vadimsh): Add fine grain ACL for tags. Tags that are set by Buildbot
81 builders should not be allowed to set by other WRITERs. 81 builders should not be allowed to set by other WRITERs.
82 """ 82 """
83 83
84 import collections 84 import collections
85 import hashlib 85 import hashlib
86 import itertools
86 import json 87 import json
87 import logging 88 import logging
88 import re 89 import re
89 import webapp2 90 import webapp2
90 91
91 from google.appengine import runtime 92 from google.appengine import runtime
92 from google.appengine.api import datastore_errors 93 from google.appengine.api import datastore_errors
93 from google.appengine.ext import ndb 94 from google.appengine.ext import ndb
94 95
95 from components import auth 96 from components import auth
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 """Returns Package entity if it exists. 159 """Returns Package entity if it exists.
159 160
160 Args: 161 Args:
161 package_name: name of the package, e.g. 'infra/tools/cipd'. 162 package_name: name of the package, e.g. 'infra/tools/cipd'.
162 163
163 Returns: 164 Returns:
164 Package or None. 165 Package or None.
165 """ 166 """
166 return package_key(package_name).get() 167 return package_key(package_name).get()
167 168
169 @staticmethod
170 def _is_in_directory(directory, path, recursive):
171 """Tests is the path is under the given directory.
Vadim Sh. 2015/06/23 00:08:34 typo: if
estaab 2015/06/23 02:26:13 Done.
172
173 This assumes directory is a prefix of path.
174
175 Args:
176 directory: String, directory the path should fall under.
Vadim Sh. 2015/06/23 00:08:35 nit: start arg desc with lower case letter
estaab 2015/06/23 02:26:13 Done.
177 path: String, full path to test.
178 recursive: Whether the path can be in a subdirectory.
179
180 Returns:
181 True if the path is under the directory.
182 """
183 start = len(directory)
184 logging.info('processing %s in %s', path, directory)
Vadim Sh. 2015/06/23 00:08:34 remove logging if it doesn't provide any additiona
estaab 2015/06/23 02:26:13 Sorry, also not supposed to be in the CL.
185
186 # The directory itself or anything shorter is not a match.
187 if len(path) <= start:
188 logging.info('shorter.')
189 return False
190
191 # The root doesn't begin with slash so only check non-root searches.
192 if start:
193 if path[start] != '/':
194 logging.info('next isnt /.')
195 return False
196 start += 1
197
198 # A subdirectory was found and we're not looking for recursive matches.
199 if not recursive and '/' in path[start:]:
200 logging.info('next after is /.')
201 return False
202 return True
203
204 def list_packages(self, dir_path, recursive):
205 """Returns lists of package names and directory names with the given prefix.
206
207 Args:
208 dir_path: string directory from which to list packages.
209 recursive: boolean whether to list contents of subdirectories.
210
211 Returns:
212 [package name, ...], [directory name, ...]
213 """
214 query = Package.query()
215
216 # Normalize directory to simplify matching logic later.
217 dir_path = dir_path.rstrip('/')
218
219 # Only apply the filtering if a prefix was given. The empty string isn't a
220 # valid key and will result in an exception.
221 if dir_path:
222 query = query.filter(
223 # Prefix match using the operators available to us. Packages can only
224 # contain lowercase ascii, numbers, and '/' so '\uffff' will always
225 # be larger.
226 ndb.AND(Package.key >= ndb.Key(Package, dir_path),
227 Package.key <= ndb.Key(Package, dir_path + u'\uffff')))
228 pkgs = []
229 dirs = set()
230 for key in query.iter(keys_only=True):
Vadim Sh. 2015/06/23 00:08:35 non-ancestor queries are eventually consistent: th
estaab 2015/06/23 02:26:13 Good to know, done. I couldn't think of an easy wa
231 pkg = key.string_id()
232 pkgs.append(pkg)
233
234 # Add in directories derived from full package path if it's not an exact
235 # match.
236 if '/' in pkg and len(dir_path) != len(pkg):
Vadim Sh. 2015/06/23 00:08:35 I don't understand len(dir_path) != len(pkg) condi
estaab 2015/06/23 02:26:13 Removed, this was from some older logic.
237 parts = pkg.split('/')
238 dirs.update('/'.join(parts[:n]) for n in xrange(1, len(parts)))
239 logging.info(str(['/'.join(parts[:n]) for n in xrange(1, len(parts))]))
Vadim Sh. 2015/06/23 00:08:34 remove logging, it's probably not helpful in relea
estaab 2015/06/23 02:26:13 Done, also a mistake.
240
241 dirs = [d for d in dirs if self._is_in_directory(dir_path, d, recursive)]
242 pkgs = [p for p in pkgs if self._is_in_directory(dir_path, p, recursive)
243 or len(dir_path) == len(p)]
244 return pkgs, dirs
245
168 def get_processing_result(self, package_name, instance_id, processor_name): 246 def get_processing_result(self, package_name, instance_id, processor_name):
169 """Returns results of some asynchronous processor or None if not ready. 247 """Returns results of some asynchronous processor or None if not ready.
170 248
171 Args: 249 Args:
172 package_name: name of the package, e.g. 'infra/tools/cipd'. 250 package_name: name of the package, e.g. 'infra/tools/cipd'.
173 instance_id: identifier of the package instance (SHA1 of package file). 251 instance_id: identifier of the package instance (SHA1 of package file).
174 processor_name: name of the processor to retrieve results of. 252 processor_name: name of the processor to retrieve results of.
175 253
176 Returns: 254 Returns:
177 ProcessingResult entity or None. 255 ProcessingResult entity or None.
(...skipping 601 matching lines...) Expand 10 before | Expand all | Expand 10 after
779 processors=payload['processors']) 857 processors=payload['processors'])
780 858
781 859
782 def get_backend_routes(): # pragma: no cover 860 def get_backend_routes(): # pragma: no cover
783 """Returns a list of webapp2.Route to add to backend WSGI app.""" 861 """Returns a list of webapp2.Route to add to backend WSGI app."""
784 return [ 862 return [
785 webapp2.Route( 863 webapp2.Route(
786 r'/internal/taskqueue/cipd-process/<instance_id:.+>', 864 r'/internal/taskqueue/cipd-process/<instance_id:.+>',
787 ProcessTaskQueueHandler), 865 ProcessTaskQueueHandler),
788 ] 866 ]
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698