Example #1
0
  def request(self, name):
    """Returns an absolute path to the directory of the named cache.

    Creates a cache directory if it does not exist yet.

    Requires NamedCache to be open.
    """
    self._lock.assert_locked()
    assert isinstance(name, basestring), name
    path = self._lru.get(name)
    create_named_link = False
    if path is None:
      path = self._allocate_dir()
      create_named_link = True
    abs_path = os.path.join(self.root_dir, path)

    file_path.ensure_tree(abs_path)
    self._lru.add(name, path)

    if create_named_link:
      # Create symlink <root_dir>/<named>/<name> -> <root_dir>/<short name>
      # for user convenience.
      named_path = self._get_named_path(name)
      if os.path.exists(named_path):
        file_path.remove(named_path)
      else:
        file_path.ensure_tree(os.path.dirname(named_path))
      fs.symlink(abs_path, named_path)

    return abs_path
Example #2
0
def link_file(outfile, infile, action):
  """Links a file. The type of link depends on |action|.

  Returns:
    True if the action was caried on, False if fallback was used.
  """
  if action not in (HARDLINK, HARDLINK_WITH_FALLBACK, SYMLINK, COPY):
    raise ValueError('Unknown mapping action %s' % action)
  if not fs.isfile(infile):
    raise OSError('%s is missing' % infile)
  if fs.isfile(outfile):
    raise OSError(
        '%s already exist; insize:%d; outsize:%d' %
        (outfile, fs.stat(infile).st_size, fs.stat(outfile).st_size))

  if action == COPY:
    readable_copy(outfile, infile)
  elif action == SYMLINK and sys.platform != 'win32':
    # On windows, symlink are converted to hardlink and fails over to copy.
    fs.symlink(infile, outfile)  # pylint: disable=E1101
  else:
    # HARDLINK or HARDLINK_WITH_FALLBACK.
    try:
      hardlink(infile, outfile)
    except OSError as e:
      if action == HARDLINK:
        raise OSError('Failed to hardlink %s to %s: %s' % (infile, outfile, e))
      # Probably a different file system.
      logging.warning(
          'Failed to hardlink, failing back to copy %s to %s' % (
            infile, outfile))
      readable_copy(outfile, infile)
      # Signal caller that fallback copy was used.
      return False
  return True
Example #3
0
def link_file(outfile, infile, action):
  """Links a file. The type of link depends on |action|.

  Returns:
    True if the action was caried on, False if fallback was used.
  """
  if action not in (HARDLINK, HARDLINK_WITH_FALLBACK, SYMLINK, COPY):
    raise ValueError('Unknown mapping action %s' % action)
  if not fs.isfile(infile):
    raise OSError('%s is missing' % infile)
  if fs.isfile(outfile):
    raise OSError(
        '%s already exist; insize:%d; outsize:%d' %
        (outfile, fs.stat(infile).st_size, fs.stat(outfile).st_size))

  if action == COPY:
    readable_copy(outfile, infile)
  elif action == SYMLINK and sys.platform != 'win32':
    # On windows, symlink are converted to hardlink and fails over to copy.
    fs.symlink(infile, outfile)  # pylint: disable=E1101
  else:
    # HARDLINK or HARDLINK_WITH_FALLBACK.
    try:
      hardlink(infile, outfile)
    except OSError as e:
      if action == HARDLINK:
        raise OSError('Failed to hardlink %s to %s: %s' % (infile, outfile, e))
      # Probably a different file system.
      logging.warning(
          'Failed to hardlink, failing back to copy %s to %s' % (
            infile, outfile))
      readable_copy(outfile, infile)
      # Signal caller that fallback copy was used.
      return False
  return True
Example #4
0
 def test_symlink_existing(self):
     # Creating a symlink that overrides a file fails.
     filepath = os.path.join(self.tempdir, 'file')
     linkfile = os.path.join(self.tempdir, 'lf')
     write_content(linkfile, 'hello')
     with self.assertRaises(OSError):
         fs.symlink(filepath, linkfile)
Example #5
0
    def test_symlink_missing_destination_abs(self):
        # A symlink to a missing destination is valid and can be read back.
        filepath = os.path.join(self.tempdir, 'file')
        linkfile = os.path.join(self.tempdir, 'lf')
        fs.symlink(filepath, linkfile)

        self.assertEqual(True, fs.islink(linkfile))
        self.assertEqual(filepath, fs.readlink(linkfile))
 def test_file_to_metadata_path_case_simple(self):
     # Ensure the symlink dest is saved in the right path case.
     subdir = os.path.join(self.cwd, u'subdir')
     fs.mkdir(subdir)
     linkdir = os.path.join(self.cwd, u'linkdir')
     fs.symlink('subDir', linkdir)
     actual = isolated_format.file_to_metadata(linkdir.upper(), False)
     self.assertEqual({'l': u'subdir'}, actual)
Example #7
0
    def uninstall(self, path, name):
        """Moves the cache directory back. Opposite to install().

    NamedCache must be open. path must be absolute and unicode.

    Raises NamedCacheError if cannot uninstall the cache.
    """
        logging.info('Uninstalling named cache %r from %r', name, path)
        with self._lock:
            try:
                if not os.path.isdir(path):
                    logging.warning(
                        'Directory %r does not exist anymore. Cache lost.',
                        path)
                    return

                rel_cache = self._lru.get(name)
                if rel_cache:
                    # Do not crash because cache already exists.
                    logging.warning('overwriting an existing named cache %r',
                                    name)
                    create_named_link = False
                else:
                    rel_cache = self._allocate_dir()
                    create_named_link = True

                # Move the dir and create an entry for the named cache.
                abs_cache = os.path.join(self.cache_dir, rel_cache)
                logging.info('Moving %r to %r', path, abs_cache)
                file_path.ensure_tree(os.path.dirname(abs_cache))
                fs.rename(path, abs_cache)
                self._lru.add(name, rel_cache)

                if create_named_link:
                    # Create symlink <cache_dir>/<named>/<name> -> <cache_dir>/<short
                    # name> for user convenience.
                    named_path = self._get_named_path(name)
                    if os.path.exists(named_path):
                        file_path.remove(named_path)
                    else:
                        file_path.ensure_tree(os.path.dirname(named_path))
                    try:
                        fs.symlink(abs_cache, named_path)
                        logging.info('Created symlink %r to %r', named_path,
                                     abs_cache)
                    except OSError:
                        # Ignore on Windows. It happens when running as a normal user or
                        # when UAC is enabled and the user is a filtered administrator
                        # account.
                        if sys.platform != 'win32':
                            raise
            except (IOError, OSError) as ex:
                raise NamedCacheError(
                    'cannot uninstall cache named %r at %r: %s' %
                    (name, path, ex))
Example #8
0
def recreate_tree(outdir, indir, infiles, action, as_hash):
  """Creates a new tree with only the input files in it.

  Arguments:
    outdir:    Output directory to create the files in.
    indir:     Root directory the infiles are based in.
    infiles:   dict of files to map from |indir| to |outdir|.
    action:    One of accepted action of file_path.link_file().
    as_hash:   Output filename is the hash instead of relfile.
  """
  logging.info(
      'recreate_tree(outdir=%s, indir=%s, files=%d, action=%s, as_hash=%s)' %
      (outdir, indir, len(infiles), action, as_hash))

  assert os.path.isabs(outdir) and outdir == os.path.normpath(outdir), outdir
  if not os.path.isdir(outdir):
    logging.info('Creating %s' % outdir)
    fs.makedirs(outdir)

  for relfile, metadata in infiles.iteritems():
    infile = os.path.join(indir, relfile)
    if as_hash:
      # Do the hashtable specific checks.
      if 'l' in metadata:
        # Skip links when storing a hashtable.
        continue
      outfile = os.path.join(outdir, metadata['h'])
      if os.path.isfile(outfile):
        # Just do a quick check that the file size matches. No need to stat()
        # again the input file, grab the value from the dict.
        if not 's' in metadata:
          raise isolated_format.MappingError(
              'Misconfigured item %s: %s' % (relfile, metadata))
        if metadata['s'] == fs.stat(outfile).st_size:
          continue
        else:
          logging.warn('Overwritting %s' % metadata['h'])
          fs.remove(outfile)
    else:
      outfile = os.path.join(outdir, relfile)
      outsubdir = os.path.dirname(outfile)
      if not os.path.isdir(outsubdir):
        fs.makedirs(outsubdir)

    if 'l' in metadata:
      pointed = metadata['l']
      logging.debug('Symlink: %s -> %s' % (outfile, pointed))
      # symlink doesn't exist on Windows.
      fs.symlink(pointed, outfile)  # pylint: disable=E1101
    else:
      file_path.link_file(outfile, infile, action)
Example #9
0
def recreate_tree(outdir, indir, infiles, action, as_hash):
  """Creates a new tree with only the input files in it.

  Arguments:
    outdir:    Output directory to create the files in.
    indir:     Root directory the infiles are based in.
    infiles:   dict of files to map from |indir| to |outdir|.
    action:    One of accepted action of file_path.link_file().
    as_hash:   Output filename is the hash instead of relfile.
  """
  logging.info(
      'recreate_tree(outdir=%s, indir=%s, files=%d, action=%s, as_hash=%s)' %
      (outdir, indir, len(infiles), action, as_hash))

  assert os.path.isabs(outdir) and outdir == os.path.normpath(outdir), outdir
  if not os.path.isdir(outdir):
    logging.info('Creating %s' % outdir)
    fs.makedirs(outdir)

  for relfile, metadata in infiles.iteritems():
    infile = os.path.join(indir, relfile)
    if as_hash:
      # Do the hashtable specific checks.
      if 'l' in metadata:
        # Skip links when storing a hashtable.
        continue
      outfile = os.path.join(outdir, metadata['h'])
      if os.path.isfile(outfile):
        # Just do a quick check that the file size matches. No need to stat()
        # again the input file, grab the value from the dict.
        if not 's' in metadata:
          raise isolated_format.MappingError(
              'Misconfigured item %s: %s' % (relfile, metadata))
        if metadata['s'] == fs.stat(outfile).st_size:
          continue
        else:
          logging.warn('Overwritting %s' % metadata['h'])
          fs.remove(outfile)
    else:
      outfile = os.path.join(outdir, relfile)
      outsubdir = os.path.dirname(outfile)
      if not os.path.isdir(outsubdir):
        fs.makedirs(outsubdir)

    if 'l' in metadata:
      pointed = metadata['l']
      logging.debug('Symlink: %s -> %s' % (outfile, pointed))
      # symlink doesn't exist on Windows.
      fs.symlink(pointed, outfile)  # pylint: disable=E1101
    else:
      file_path.link_file(outfile, infile, action)
Example #10
0
def link_file(outfile, infile, action):
  """Links a file. The type of link depends on |action|.

  Returns:
    True if the action was carried on, False if fallback was used.
  """
  if action < 1 or action > COPY:
    raise ValueError('Unknown mapping action %s' % action)
  # TODO(maruel): Skip these checks.
  if not fs.isfile(infile):
    raise OSError('%s is missing' % infile)
  if fs.isfile(outfile):
    raise OSError(
        '%s already exist; insize:%d; outsize:%d' %
        (outfile, fs.stat(infile).st_size, fs.stat(outfile).st_size))

  if action == COPY:
    readable_copy(outfile, infile)
    return True

  if action in (SYMLINK, SYMLINK_WITH_FALLBACK):
    try:
      fs.symlink(infile, outfile)  # pylint: disable=E1101
      return True
    except OSError:
      if action == SYMLINK:
        raise
      logging.warning(
          'Failed to symlink, falling back to copy %s to %s' % (
            infile, outfile))
      # Signal caller that fallback copy was used.
      readable_copy(outfile, infile)
      return False

  # HARDLINK or HARDLINK_WITH_FALLBACK.
  try:
    hardlink(infile, outfile)
    return True
  except OSError as e:
    if action == HARDLINK:
      raise OSError('Failed to hardlink %s to %s: %s' % (infile, outfile, e))

  # Probably a different file system.
  logging.warning(
      'Failed to hardlink, falling back to copy %s to %s' % (
        infile, outfile))
  readable_copy(outfile, infile)
  # Signal caller that fallback copy was used.
  return False
Example #11
0
def make_tree(out, contents):
  for relpath, content in sorted(contents.iteritems()):
    filepath = os.path.join(out, relpath.replace('/', os.path.sep))
    dirpath = os.path.dirname(filepath)
    if not fs.isdir(dirpath):
      fs.makedirs(dirpath, 0700)
    if isinstance(content, SymLink):
      fs.symlink(content, filepath)
    else:
      mode = 0700 if relpath.endswith('.py') else 0600
      flags = os.O_WRONLY | os.O_CREAT
      if sys.platform == 'win32':
        # pylint: disable=no-member
        flags |= os.O_BINARY
      with os.fdopen(os.open(filepath, flags, mode), 'wb') as f:
        f.write(content)
    def test_symlink_absolute(self):
        # A symlink to an absolute path is valid.
        # /dir
        # /dir/file
        # /ld -> /dir
        # /lf -> /ld/file
        dirpath = os.path.join(self.tempdir, 'dir')
        filepath = os.path.join(dirpath, 'file')
        fs.mkdir(dirpath)
        write_content(filepath, b'hello')

        linkfile = os.path.join(self.tempdir, 'lf')
        linkdir = os.path.join(self.tempdir, 'ld')
        dstfile = os.path.join(linkdir, 'file')
        fs.symlink(dstfile, linkfile)
        fs.symlink(dirpath, linkdir)

        self.assertEqual(True, fs.islink(linkfile))
        self.assertEqual(True, fs.islink(linkdir))
        self.assertEqual(dstfile, fs.readlink(linkfile))
        self.assertEqual(dirpath, fs.readlink(linkdir))
        self.assertEqual(['file'], fs.listdir(linkdir))
        # /lf resolves to /dir/file.
        with fs.open(linkfile) as f:
            self.assertEqual('hello', f.read())

        # Ensures that followlinks is respected in walk().
        expected = [
            (self.tempdir, ['dir', 'ld'], ['lf']),
            (dirpath, [], ['file']),
        ]
        actual = [
            (r, sorted(d), sorted(f))
            for r, d, f in sorted(fs.walk(self.tempdir, followlinks=False))
        ]
        self.assertEqual(expected, actual)
        expected = [
            (self.tempdir, ['dir', 'ld'], ['lf']),
            (dirpath, [], ['file']),
            (linkdir, [], ['file']),
        ]
        actual = [
            (r, sorted(d), sorted(f))
            for r, d, f in sorted(fs.walk(self.tempdir, followlinks=True))
        ]
        self.assertEqual(expected, actual)
Example #13
0
  def uninstall(self, path, name):
    """Moves the cache directory back. Opposite to install().

    NamedCache must be open. path must be absolute and unicode.

    Raises Error if cannot uninstall the cache.
    """
    logging.info('Uninstalling named cache %r from %r', name, path)
    try:
      _check_abs(path)
      if not os.path.isdir(path):
        logging.warning(
            'Directory %r does not exist anymore. Cache lost.', path)
        return

      rel_cache = self._lru.get(name)
      if rel_cache:
        # Do not crash because cache already exists.
        logging.warning('overwriting an existing named cache %r', name)
        create_named_link = False
      else:
        rel_cache = self._allocate_dir()
        create_named_link = True

      # Move the dir and create an entry for the named cache.
      abs_cache = os.path.join(self.root_dir, rel_cache)
      logging.info('Moving %r to %r', path, abs_cache)
      file_path.ensure_tree(os.path.dirname(abs_cache))
      fs.rename(path, abs_cache)
      self._lru.add(name, rel_cache)

      if create_named_link:
        # Create symlink <root_dir>/<named>/<name> -> <root_dir>/<short name>
        # for user convenience.
        named_path = self._get_named_path(name)
        if os.path.exists(named_path):
          file_path.remove(named_path)
        else:
          file_path.ensure_tree(os.path.dirname(named_path))
        fs.symlink(abs_cache, named_path)
        logging.info('Created symlink %r to %r', named_path, abs_cache)
    except (OSError, Error) as ex:
      raise Error(
          'cannot uninstall cache named %r at %r: %s' % (
            name, path, ex))
Example #14
0
    def test_cleanup_incorrect_link(self):
        cache = self.get_cache(_get_policies())
        self._add_one_item(cache, 1)
        self._add_one_item(cache, 2)
        fs.remove(os.path.join(self.cache_dir, cache.NAMED_DIR, u'1'))
        fs.remove(os.path.join(self.cache_dir, cache.NAMED_DIR, u'2'))
        fs.symlink('invalid_dest',
                   os.path.join(self.cache_dir, cache.NAMED_DIR, u'1'))
        os.mkdir(os.path.join(self.cache_dir, cache.NAMED_DIR, u'2'))

        cache = self.get_cache(_get_policies())
        self.assertEqual(
            ['1', '2'],
            sorted(fs.listdir(os.path.join(cache.cache_dir, cache.NAMED_DIR))))
        self.assertEqual(True, cache.cleanup())
        self.assertEqual([],
                         fs.listdir(
                             os.path.join(cache.cache_dir, cache.NAMED_DIR)))
Example #15
0
        def test_expand_symlinks_path_case(self):
            # Ensures that the resulting path case is fixed on case insensitive file
            # system.
            fs.symlink('dest', os.path.join(self.cwd, u'link'))
            fs.mkdir(os.path.join(self.cwd, u'Dest'))
            fs.open(os.path.join(self.cwd, u'Dest', u'file.txt'), 'w').close()

            relfile, symlinks = isolated_format._expand_symlinks(
                self.cwd, u'.')
            self.assertEqual((u'.', []), (relfile, symlinks))

            relfile, symlinks = isolated_format._expand_symlinks(
                self.cwd, u'link')
            self.assertEqual((u'Dest', [u'link']), (relfile, symlinks))

            relfile, symlinks = isolated_format._expand_symlinks(
                self.cwd, u'link/File.txt')
            self.assertEqual((u'Dest/file.txt', [u'link']),
                             (relfile, symlinks))
Example #16
0
 def test_cleanup_unexpected_named(self):
     os.mkdir(self.cache_dir)
     c = local_caching.NamedCache
     os.mkdir(os.path.join(self.cache_dir, c.NAMED_DIR))
     p = os.path.join(self.cache_dir, c.NAMED_DIR, u'junk_file')
     with open(p, 'w') as f:
         f.write('random')
     os.mkdir(os.path.join(self.cache_dir, c.NAMED_DIR, u'junk_dir'))
     fs.symlink('invalid_dest',
                os.path.join(self.cache_dir, c.NAMED_DIR, u'junk_link'))
     cache = self.get_cache(_get_policies())
     self.assertEqual([cache.NAMED_DIR], fs.listdir(cache.cache_dir))
     self.assertEqual(
         ['junk_dir', 'junk_file', 'junk_link'],
         sorted(fs.listdir(os.path.join(cache.cache_dir, cache.NAMED_DIR))))
     self.assertEqual(True, cache.cleanup())
     self.assertEqual([cache.NAMED_DIR, cache.STATE_FILE],
                      sorted(fs.listdir(cache.cache_dir)))
     self.assertEqual([],
                      fs.listdir(
                          os.path.join(cache.cache_dir, cache.NAMED_DIR)))
Example #17
0
 def test_symlink_input_absolute_path(self):
     # A symlink is outside of the checkout, it should be treated as a normal
     # directory.
     # .../src
     # .../src/out -> .../tmp/foo
     # .../tmp
     # .../tmp/foo
     src = os.path.join(self.cwd, u'src')
     src_out = os.path.join(src, u'out')
     tmp = os.path.join(self.cwd, u'tmp')
     tmp_foo = os.path.join(tmp, u'foo')
     fs.mkdir(src)
     fs.mkdir(tmp)
     fs.mkdir(tmp_foo)
     # The problem was that it's an absolute path, so it must be considered a
     # normal directory.
     fs.symlink(tmp, src_out)
     fs.open(os.path.join(tmp_foo, u'bar.txt'), 'w').close()
     relfile, symlinks = isolated_format._expand_symlinks(
         src, u'out/foo/bar.txt')
     self.assertEqual((u'out/foo/bar.txt', []), (relfile, symlinks))
Example #18
0
        def test_file_to_metadata_path_case_complex(self):
            # Ensure the symlink dest is saved in the right path case. This includes 2
            # layers of symlinks.
            basedir = os.path.join(self.cwd, u'basebir')
            fs.mkdir(basedir)

            linkeddir2 = os.path.join(self.cwd, u'linkeddir2')
            fs.mkdir(linkeddir2)

            linkeddir1 = os.path.join(basedir, u'linkeddir1')
            fs.symlink('../linkedDir2', linkeddir1)

            subsymlinkdir = os.path.join(basedir, u'symlinkdir')
            fs.symlink('linkedDir1', subsymlinkdir)

            actual = isolated_format.file_to_metadata(subsymlinkdir.upper(),
                                                      True, False)
            self.assertEqual({'l': u'linkeddir1'}, actual)

            actual = isolated_format.file_to_metadata(linkeddir1.upper(), True,
                                                      False)
            self.assertEqual({'l': u'../linkeddir2'}, actual)
Example #19
0
 def test_archive_files_to_storage_symlink(self):
   link_path = os.path.join(self.tempdir, u'link')
   with open(os.path.join(self.tempdir, u'foo'), 'wb') as f:
     f.write('fooo')
   fs.symlink('foo', link_path)
   server_ref = isolate_storage.ServerRef('http://localhost:1', 'default')
   storage_api = MockedStorageApi(server_ref, {})
   storage = isolateserver.Storage(storage_api)
   results, cold, hot = isolateserver.archive_files_to_storage(
       storage, [self.tempdir], None)
   self.assertEqual([self.tempdir], results.keys())
   self.assertEqual([], cold)
   # isolated, symlink, foo file.
   self.assertEqual(3, len(hot))
   self.assertEqual(os.path.join(self.tempdir, u'foo'), hot[0].path)
   self.assertEqual(4, hot[0].size)
   # TODO(maruel): The symlink is reported as its destination. We should fix
   # this because it double counts the stats.
   self.assertEqual(os.path.join(self.tempdir, u'foo'), hot[1].path)
   self.assertEqual(4, hot[1].size)
   # The isolated file is pure in-memory.
   self.assertIsInstance(hot[2], isolateserver.BufferItem)
Example #20
0
  def create_symlinks(self, root, named_caches):
    """Creates symlinks in |root| for specified named_caches.

    named_caches must be a list of (name, path) tuples.

    Requires NamedCache to be open.

    Raises Error if cannot create a symlink.
    """
    self._lock.assert_locked()
    for name, path in named_caches:
      try:
        if os.path.isabs(path):
          raise Error('named cache path must not be absolute')
        if '..' in path.split(os.path.sep):
          raise Error('named cache path must not contain ".."')
        symlink_path = os.path.abspath(os.path.join(root, path))
        file_path.ensure_tree(os.path.dirname(symlink_path))
        fs.symlink(self.request(name), symlink_path)
      except (OSError, Error) as ex:
        raise Error(
            'cannot create a symlink for cache named "%s" at "%s": %s' % (
              name, symlink_path, ex))
Example #21
0
    def create_symlinks(self, root, named_caches):
        """Creates symlinks in |root| for the specified named_caches.

    named_caches must be a list of (name, path) tuples.

    Requires NamedCache to be open.

    Raises Error if cannot create a symlink.
    """
        self._lock.assert_locked()
        for name, path in named_caches:
            logging.info('Named cache %r -> %r', name, path)
            try:
                _validate_named_cache_path(path)
                symlink_path = os.path.abspath(os.path.join(root, path))
                file_path.ensure_tree(os.path.dirname(symlink_path))
                requested = self.request(name)
                logging.info('Symlink %r to %r', symlink_path, requested)
                fs.symlink(requested, symlink_path)
            except (OSError, Error) as ex:
                raise Error(
                    'cannot create a symlink for cache named "%s" at "%s": %s'
                    % (name, symlink_path, ex))
Example #22
0
        def test_file_to_metadata_path_case_collapse(self):
            # Ensure setting the collapse_symlink option doesn't include the symlinks
            basedir = os.path.join(self.cwd, u'basedir')
            fs.mkdir(basedir)
            subdir = os.path.join(basedir, u'subdir')
            fs.mkdir(subdir)
            linkdir = os.path.join(basedir, u'linkdir')
            fs.mkdir(linkdir)

            foo_file = os.path.join(subdir, u'Foo.txt')
            fs.open(foo_file, 'w').close()
            sym_file = os.path.join(basedir, u'linkdir', u'Sym.txt')
            fs.symlink('../subdir/Foo.txt', sym_file)

            actual = isolated_format.file_to_metadata(sym_file, True, True)
            actual['h'] = isolated_format.hash_file(sym_file, ALGO)
            expected = {
                # SHA-1 of empty string
                'h': 'da39a3ee5e6b4b0d3255bfef95601890afd80709',
                'm': 256,
                's': 0,
            }
            self.assertEqual(expected, actual)
Example #23
0
    def uninstall(self, path, name):
        """Moves the cache directory back. Opposite to install().

    path must be absolute and unicode.

    Raises NamedCacheError if cannot uninstall the cache.
    """
        logging.info('Uninstalling named cache %r from %r', name, path)
        with self._lock:
            try:
                if not os.path.isdir(path):
                    logging.warning(
                        'Directory %r does not exist anymore. Cache lost.',
                        path)
                    return

                if name in self._lru:
                    # This shouldn't happen but just remove the preexisting one and move
                    # on.
                    logging.warning('overwriting an existing named cache %r',
                                    name)
                    self._remove(name)
                rel_cache = self._allocate_dir()

                # Move the dir and create an entry for the named cache.
                abs_cache = os.path.join(self.cache_dir, rel_cache)
                logging.info('Moving %r to %r', path, abs_cache)
                file_path.ensure_tree(os.path.dirname(abs_cache))
                fs.rename(path, abs_cache)

                # That succeeded, calculate its new size.
                size = _get_recursive_size(abs_cache)
                if not size:
                    # Do not save empty named cache.
                    return
                self._lru.add(name, (rel_cache, size))
                self._added.append(size)

                # Create symlink <cache_dir>/<named>/<name> -> <cache_dir>/<short name>
                # for user convenience.
                named_path = self._get_named_path(name)
                if os.path.exists(named_path):
                    file_path.remove(named_path)
                else:
                    file_path.ensure_tree(os.path.dirname(named_path))

                try:
                    fs.symlink(abs_cache, named_path)
                    logging.info('Created symlink %r to %r', named_path,
                                 abs_cache)
                except OSError:
                    # Ignore on Windows. It happens when running as a normal user or when
                    # UAC is enabled and the user is a filtered administrator account.
                    if sys.platform != 'win32':
                        raise
            except (IOError, OSError) as ex:
                raise NamedCacheError(
                    'cannot uninstall cache named %r at %r: %s' %
                    (name, path, ex))
            finally:
                self._save()
    def uninstall(self, src, name):
        """Moves the cache directory back into the named cache hive for an eventual
    reuse.

    The opposite of install().

    src must be absolute and unicode. Its content is moved back into the local
    named caches cache.

    Returns the named cache size in bytes.

    Raises NamedCacheError if cannot uninstall the cache.
    """
        logging.info('NamedCache.uninstall(%r, %r)', src, name)
        with self._lock:
            try:
                if not fs.isdir(src):
                    logging.warning(
                        'NamedCache: Directory %r does not exist anymore. Cache lost.',
                        src)
                    return

                if name in self._lru:
                    # This shouldn't happen but just remove the preexisting one and move
                    # on.
                    logging.error('- overwriting existing cache!')
                    self._remove(name)

                # Calculate the size of the named cache to keep. It's important because
                # if size is zero (it's empty), we do not want to add it back to the
                # named caches cache.
                size = _get_recursive_size(src)
                logging.info('- Size is %d', size)
                if not size:
                    # Do not save empty named cache.
                    return size

                # Move the dir and create an entry for the named cache.
                rel_cache = self._allocate_dir()
                abs_cache = os.path.join(self.cache_dir, rel_cache)
                logging.info('- Moving to %r', rel_cache)
                file_path.ensure_tree(os.path.dirname(abs_cache))
                fs.rename(src, abs_cache)

                self._lru.add(name, (rel_cache, size))
                self._added.append(size)

                # Create symlink <cache_dir>/<named>/<name> -> <cache_dir>/<short name>
                # for user convenience.
                named_path = self._get_named_path(name)
                if fs.exists(named_path):
                    file_path.remove(named_path)
                else:
                    file_path.ensure_tree(os.path.dirname(named_path))

                try:
                    fs.symlink(os.path.join(u'..', rel_cache), named_path)
                    logging.info('NamedCache: Created symlink %r to %r',
                                 named_path, abs_cache)
                except OSError:
                    # Ignore on Windows. It happens when running as a normal user or when
                    # UAC is enabled and the user is a filtered administrator account.
                    if sys.platform != 'win32':
                        raise
                return size
            except (IOError, OSError) as ex:
                # Raise using the original traceback.
                exc = NamedCacheError(
                    'cannot uninstall cache named %r at %r: %s' %
                    (name, src, ex))
                six.reraise(exc, None, sys.exc_info()[2])
            finally:
                # Call save() at every uninstall. The assumptions are:
                # - The total the number of named caches is low, so the state.json file
                #   is small, so the time it takes to write it to disk is short.
                # - The number of mapped named caches per task is low, so the number of
                #   times save() is called on tear-down isn't high enough to be
                #   significant.
                # - uninstall() sometimes throws due to file locking on Windows or
                #   access rights on Linux. We want to keep as many as possible.
                self._save()