| OLD | NEW |
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 """This file implements Named Caches.""" | 5 """This file implements Named Caches.""" |
| 6 | 6 |
| 7 import contextlib | 7 import contextlib |
| 8 import logging | 8 import logging |
| 9 import optparse | 9 import optparse |
| 10 import os | 10 import os |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 """Returns timestamp of last use of an item. | 134 """Returns timestamp of last use of an item. |
| 135 | 135 |
| 136 Requires NamedCache to be open. | 136 Requires NamedCache to be open. |
| 137 | 137 |
| 138 Raises KeyError if cache is not found. | 138 Raises KeyError if cache is not found. |
| 139 """ | 139 """ |
| 140 self._lock.assert_locked() | 140 self._lock.assert_locked() |
| 141 assert isinstance(name, basestring), name | 141 assert isinstance(name, basestring), name |
| 142 return self._lru.get_timestamp(name) | 142 return self._lru.get_timestamp(name) |
| 143 | 143 |
| 144 @contextlib.contextmanager |
| 144 def create_symlinks(self, root, named_caches): | 145 def create_symlinks(self, root, named_caches): |
| 145 """Creates symlinks in |root| for specified named_caches. | 146 """Creates symlinks in |root| for the specified named_caches. |
| 146 | 147 |
| 147 named_caches must be a list of (name, path) tuples. | 148 named_caches must be a list of (name, path) tuples. |
| 148 | 149 |
| 149 Requires NamedCache to be open. | 150 Requires NamedCache to be open. |
| 150 | 151 |
| 151 Raises Error if cannot create a symlink. | 152 Raises Error if cannot create a symlink. |
| 152 """ | 153 """ |
| 153 self._lock.assert_locked() | 154 self._lock.assert_locked() |
| 154 for name, path in named_caches: | 155 for name, path in named_caches: |
| 155 logging.info('Named cache %r -> %r', name, path) | 156 logging.info('Named cache %r -> %r', name, path) |
| 156 try: | 157 try: |
| 157 if os.path.isabs(path): | 158 _validate_named_cache_path(path) |
| 158 raise Error('named cache path must not be absolute') | |
| 159 if '..' in path.split(os.path.sep): | |
| 160 raise Error('named cache path must not contain ".."') | |
| 161 symlink_path = os.path.abspath(os.path.join(root, path)) | 159 symlink_path = os.path.abspath(os.path.join(root, path)) |
| 162 file_path.ensure_tree(os.path.dirname(symlink_path)) | 160 file_path.ensure_tree(os.path.dirname(symlink_path)) |
| 163 requested = self.request(name) | 161 requested = self.request(name) |
| 164 logging.info('Symlink %r to %r', symlink_path, requested) | 162 logging.info('Symlink %r to %r', symlink_path, requested) |
| 165 fs.symlink(requested, symlink_path) | 163 fs.symlink(requested, symlink_path) |
| 166 except (OSError, Error) as ex: | 164 except (OSError, Error) as ex: |
| 167 raise Error( | 165 raise Error( |
| 168 'cannot create a symlink for cache named "%s" at "%s": %s' % ( | 166 'cannot create a symlink for cache named "%s" at "%s": %s' % ( |
| 169 name, symlink_path, ex)) | 167 name, symlink_path, ex)) |
| 170 | 168 |
| 169 def delete_symlinks(self, root, named_caches): |
| 170 """Deletes symlinks from |root| for the specified named_caches. |
| 171 |
| 172 named_caches must be a list of (name, path) tuples. |
| 173 """ |
| 174 for name, path in named_caches: |
| 175 logging.info('Unlinking named cache "%s"', name) |
| 176 try: |
| 177 _validate_named_cache_path(path) |
| 178 symlink_path = os.path.abspath(os.path.join(root, path)) |
| 179 fs.unlink(symlink_path) |
| 180 except (OSError, Error) as ex: |
| 181 raise Error( |
| 182 'cannot unlink cache named "%s" at "%s": %s' % ( |
| 183 name, symlink_path, ex)) |
| 184 |
| 171 def trim(self, min_free_space): | 185 def trim(self, min_free_space): |
| 172 """Purges cache. | 186 """Purges cache. |
| 173 | 187 |
| 174 Removes cache directories that were not accessed for a long time | 188 Removes cache directories that were not accessed for a long time |
| 175 until there is enough free space and the number of caches is sane. | 189 until there is enough free space and the number of caches is sane. |
| 176 | 190 |
| 177 If min_free_space is None, disk free space is not checked. | 191 If min_free_space is None, disk free space is not checked. |
| 178 | 192 |
| 179 Requires NamedCache to be open. | 193 Requires NamedCache to be open. |
| 180 """ | 194 """ |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 255 parser.error('--named-cache is specified, but --named-cache-root is empty') | 269 parser.error('--named-cache is specified, but --named-cache-root is empty') |
| 256 for name, path in options.named_caches: | 270 for name, path in options.named_caches: |
| 257 if not CACHE_NAME_RE.match(name): | 271 if not CACHE_NAME_RE.match(name): |
| 258 parser.error( | 272 parser.error( |
| 259 'cache name "%s" does not match %s' % (name, CACHE_NAME_RE.pattern)) | 273 'cache name "%s" does not match %s' % (name, CACHE_NAME_RE.pattern)) |
| 260 if not path: | 274 if not path: |
| 261 parser.error('cache path cannot be empty') | 275 parser.error('cache path cannot be empty') |
| 262 if options.named_cache_root: | 276 if options.named_cache_root: |
| 263 return CacheManager(os.path.abspath(options.named_cache_root)) | 277 return CacheManager(os.path.abspath(options.named_cache_root)) |
| 264 return None | 278 return None |
| 279 |
| 280 |
| 281 def _validate_named_cache_path(path): |
| 282 if os.path.isabs(path): |
| 283 raise Error('named cache path must not be absolute') |
| 284 if '..' in path.split(os.path.sep): |
| 285 raise Error('named cache path must not contain ".."') |
| OLD | NEW |