| Index: chrome/common/extensions/docs/server2/revision_pinner.py
|
| diff --git a/chrome/common/extensions/docs/server2/revision_pinner.py b/chrome/common/extensions/docs/server2/revision_pinner.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..808e5872503967f981346128c2e408f6e8df77cc
|
| --- /dev/null
|
| +++ b/chrome/common/extensions/docs/server2/revision_pinner.py
|
| @@ -0,0 +1,126 @@
|
| +# Copyright 2013 The Chromium Authors. All rights reserved.
|
| +# Use of this server2 code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +import logging
|
| +from object_store_creator import ObjectStoreCreator
|
| +import random
|
| +
|
| +class _Info(object):
|
| + '''Info about the revision pinning. Properties:
|
| + |server2_version| The last known version of server2 source.
|
| + |fs_version | The current safe version of the file system. This isn't
|
| + necessarily related to |server2_version|.
|
| + |pinned| None if there is no pin. The password (string) to unpin it
|
| + if it is pinned.
|
| + '''
|
| + def __init__(self, server2_version, root_version, pinned=None):
|
| + self.server2_version = server2_version
|
| + self.root_version = root_version
|
| + self.pinned = pinned
|
| +
|
| +# TODO(kalman): RevisionInfo? VersionInfo? VersionPinner?
|
| +class RevisionPinner(object):
|
| + '''Tracks the subversion version that each branch is pinned to.
|
| +
|
| + Responsible for pausing branches when any of the directories that server2
|
| + server2 is pulled from changes.
|
| +
|
| + Generates and exposes a method to unpin those values.
|
| + '''
|
| + @staticmethod
|
| + def CreateForSubversion(branch,
|
| + svn_file_system,
|
| + object_store_creator_factory):
|
| + server2_dirs = [
|
| + 'chrome/common/extensions/docs/server2/',
|
| + 'third_party/handlebar/',
|
| + 'tools/json_schema_compiler/'
|
| + 'tools/json_schema_eater/'
|
| + ]
|
| + return RevisionPinner(branch,
|
| + svn_file_system,
|
| + object_store_creator_factory,
|
| + server2_dirs)
|
| +
|
| + def __init__(self,
|
| + branch,
|
| + file_system,
|
| + object_store_creator_factory,
|
| + server2_dirs):
|
| + self._branch = branch
|
| + self._file_system = file_system
|
| + self._object_store = (
|
| + object_store_creator_factory.Create(RevisionPinner).Create(
|
| + category=file_system.GetName()))
|
| + self._server2_dirs = server2_dirs
|
| +
|
| + def Update(self):
|
| + '''Checks to see whether there have been any changes to the server2 dirs.
|
| + If so, pin at the current version with an option to unpin.
|
| + Otherwise, bump the version.
|
| + '''
|
| + server2_version = max(
|
| + int(self._file_system.Stat(server2_dir).version)
|
| + for server2_dir in self._server2_dirs)
|
| + root_version = int(self._file_system.Stat('/').version)
|
| + logging.info('server2_version: %s, root_version: %s' % (
|
| + server2_version, root_version))
|
| +
|
| + info = self._object_store.Get('info')
|
| +
|
| + if info is None:
|
| + logging.info('No existing info')
|
| + info = _Info(server2_version, root_version)
|
| + elif info.pinned:
|
| + logging.info('Still pinned at %s' % info.root_version)
|
| + info = _Info(server2_version, info.root_version, pinned=info.pinned)
|
| + elif server2_version <= info.server2_version:
|
| + logging.info('No change in server2 version')
|
| + # Ok, the version probably isn't going to *regress* but be safe anyway.
|
| + # Who knows what might happen to SVN.
|
| + info = _Info(server2_version, root_version)
|
| + else:
|
| + logging.info('server2 version increased from %s to %s' % (
|
| + server2_version, info.server2_version))
|
| + # New version of server2! Pin it: root_version doesn't increase, and
|
| + # the pin bit is flipped.
|
| + password = self._GeneratePassword()
|
| + info = _Info(server2_version, info.root_version, pinned=password)
|
| + self._NotifyPinned(info.root_version, password)
|
| +
|
| + self._object_store.Set('info', info)
|
| +
|
| + def _GeneratePassword(self):
|
| + # TODO(kalman): generate a prettier password using full alphanumerics.
|
| + return ''.join(map(str, random.sample(xrange(100, 999), 10)))
|
| +
|
| + def _OnPinned(self, revision, pinned):
|
| + '''Called when the file system is pinned at |revision| using |password|.
|
| + Set up a way to unpin it: send email to kalman@ with an unlock pin.
|
| + '''
|
| + logging.warning('PINNED AT %s' % self._GenerateUnlockURL(pinned))
|
| +
|
| + def _GenerateUnlockURL(self, pinned):
|
| + assert pinned
|
| + # TODO(kalman): Include hostname.
|
| + return '/_unpin/%s/%s' % (branch, pinned)
|
| +
|
| + def GetRevision(self):
|
| + '''Gets the revision that |file_system| is currently pinned at.
|
| + '''
|
| + return self._object_store.Get('info').Get().root_version
|
| +
|
| + def GetPinned(self):
|
| + '''Gets the pinned password if |file_system| is pinned, None if not.
|
| + '''
|
| + return self._object_store.Get('info').Get().pinned
|
| +
|
| + def Unpin(self, password):
|
| + '''Unpins using |password|. Returns true if successful, false if not.
|
| + '''
|
| + if self.GetPinned() != password:
|
| + return False
|
| + self._object_store.Del('info')
|
| + self.Update()
|
| + return True
|
|
|