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 |