]> scripts.mit.edu Git - wizard.git/commitdiff
Implement locks for upgrade, add d.upgrade()
authorEdward Z. Yang <ezyang@mit.edu>
Sat, 3 Oct 2009 05:51:59 +0000 (01:51 -0400)
committerEdward Z. Yang <ezyang@mit.edu>
Sat, 3 Oct 2009 05:51:59 +0000 (01:51 -0400)
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
TODO
wizard/app/mediawiki.py
wizard/command/migrate.py
wizard/command/upgrade.py
wizard/deploy.py
wizard/util.py

diff --git a/TODO b/TODO
index d1b0f521beb20faae8539ada2521ae49d5240c0a..37c6a00e3690b49e89eab0b06d86448c552e02ff 100644 (file)
--- a/TODO
+++ b/TODO
@@ -15,13 +15,9 @@ TODO NOW:
   to determine version.  Make parallel-find.pl have this have greater
   precedence.  This also means, however, that we get
   full mediawiki-1.2.3-2-abcdef names (Have patch, pending testing and commit)
-- Make the installer use 'wizard install' /or/ do a migration
+- Make deployed installer use 'wizard install' /or/ do a migration
   after doing a normal install (the latter makes it easier
   for mass-rollbacks).
-- Have the upgrader do locking (.scripts/lock, probably)
-
-- Relax MediaWiki regexes to terminate on semicolon, and not
-  require its own line.
 
 - Better error message if daemon/scripts-security-upd
   is not on scripts-security-upd list
@@ -32,8 +28,6 @@ TODO NOW:
   do in both directions).  All 1.11.0 installs except four have
   the other (check diff -u with all in /root)
 
-- Make upgrade and install take version as a parameter
-
 - Redo Wordpress conversion, with an eye for automating everything
   possible (such as downloading the tarball and unpacking)
 
index bfac9c7585cf4fb2ccc5dad7ce916ef623b0cbdf..6b39c8fd54d5721888c2a51ef13dde55a1fefc4b 100644 (file)
@@ -84,7 +84,7 @@ class Application(deploy.Application):
         if result.find("Installation successful") == -1:
             raise install.Failure()
         os.rename('config/LocalSettings.php', 'LocalSettings.php')
-    def upgrade(self, version, options):
+    def upgrade(self, d, version, options):
         sh = shell.Shell()
         if not os.path.isfile("AdminSettings.php"):
             sh.call("git", "checkout", "mediawiki-" + str(version), "--", "AdminSettings.php")
index 4e5837d73d30ae0439d135bc59759a0af559a8ab..3949ae8ced0dd1b4d8242009dd5582aa5b8ec159 100644 (file)
@@ -47,16 +47,8 @@ def main(argv, baton):
     tag     = version.scripts_tag
 
     # XXX: turn this into a context
-    try:
+    with util.LockDirectory(".scripts-migrate-lock"):
         try:
-            try:
-                os.open(".scripts-migrate-lock", os.O_CREAT | os.O_EXCL)
-            except OSError as e:
-                if e.errno == errno.EEXIST:
-                    raise DirectoryLockedError
-                elif e.errno == errno.EACCES:
-                    raise command.PermissionsError(dir)
-                raise
             if options.force: perform_force(options)
             make_repository(sh, options, repo, tag)
             check_variables(deployment, options)
@@ -67,11 +59,6 @@ def main(argv, baton):
                 shutil.rmtree(".scripts")
             if os.path.exists(".git"):
                 shutil.rmtree(".git")
-    finally:
-        try:
-            os.unlink(".scripts-migrate-lock")
-        except OSError:
-            pass
 
 def parse_args(argv, baton):
     usage = """usage: %prog migrate [ARGS] DIR
@@ -174,13 +161,3 @@ ERROR: Directory already contains a .git and
 both of these directories will be removed.
 """
 
-class DirectoryLockedError(Error):
-    def __init__(self, dir):
-        self.dir = dir
-    def __str__(self):
-        return """
-
-ERROR: Could not acquire lock on directory.  Maybe there is
-another migration process running?
-"""
-
index 235059269d3ab356257902bf45137e74d20fa4bb..051ee22efc62cc7e14586b71d8c4007840b1a164 100644 (file)
@@ -78,28 +78,27 @@ def main(argv, baton):
     # perform database backup
     backup = d.backup(options)
     # XXX: frob .htaccess to make site inaccessible
-    # XXX: need locking
-    # git merge (which performs a fast forward)
-    #   - merge could fail (race); that's /really/ dangerous.
     with util.IgnoreKeyboardInterrupts():
-        sh.call("git", "pull", temp_wc_dir, "master")
-        version_obj = distutils.version.LooseVersion(version.partition('-')[2])
-        try:
-            # run update script
-            d.application.upgrade(version_obj, options)
-            d.verifyWeb()
-        except app.UpgradeFailure:
-            logging.warning("Upgrade failed: rolling back")
-            perform_restore(d, backup)
-            raise
-        except deploy.WebVerificationError:
-            logging.warning("Web verification failed: rolling back")
-            perform_restore(d, backup)
-            raise app.UpgradeFailure("Upgrade caused website to become inaccessible; site was rolled back")
+        with util.LockDirectory(".scripts-upgrade-lock"):
+            # git merge (which performs a fast forward)
+            sh.call("git", "pull", temp_wc_dir, "master")
+            version_obj = distutils.version.LooseVersion(version.partition('-')[2])
+            try:
+                # run update script
+                d.upgrade(version_obj, options)
+                d.verifyWeb()
+            except app.UpgradeFailure:
+                logging.warning("Upgrade failed: rolling back")
+                perform_restore(d, backup)
+                raise
+            except deploy.WebVerificationError:
+                logging.warning("Web verification failed: rolling back")
+                perform_restore(d, backup)
+                raise app.UpgradeFailure("Upgrade caused website to become inaccessible; site was rolled back")
     # XXX: frob .htaccess to make site accessible
-    # XXX:  - check if .htaccess changed, first.  Upgrade
+    #       to do this, check if .htaccess changed, first.  Upgrade
     #       process might have frobbed it.  Don't be
-    #       particularly worried if the segment dissappeared
+    #       particularly worried if the segment disappeared
 
 def perform_restore(d, backup):
     # You don't want d.restore() because it doesn't perform
index 0676454a90f0296e4e450b8e0dc0b87f82f56ce8..f2b37290eddeae910717dfe3fc42fd42c6382a52 100644 (file)
@@ -105,6 +105,12 @@ class Deployment(object):
         as such dir will generally not equal :attr:`location`.
         """
         return self.application.parametrize(self, dir)
+    def upgrade(self, version, options):
+        """
+        Performs an upgrae of database schemas and other non-versioned data.
+        """
+        with util.ChangeDirectory(self.location):
+            return self.application.upgrade(self, version, options)
     def backup(self, options):
         """
         Performs a backup of database schemas and other non-versioned data.
@@ -383,7 +389,7 @@ class Application(object):
         working directory is a deployment.
         """
         raise NotImplemented
-    def upgrade(self, version, options):
+    def upgrade(self, deployment, version, options):
         """
         Run for 'wizard upgrade' to upgrade database schemas and other
         non-versioned data in an application.  This assumes that
index 621dea16d61a9d4b36292562c04c1b32d726c1d4..2d9c72f118467f9cbc14525acd237927b3a87b08 100644 (file)
@@ -83,6 +83,27 @@ class IgnoreKeyboardInterrupts(object):
     def __exit__(self, *args):
         signal.signal(signal.SIGINT, signal.default_int_handler)
 
+class LockDirectory(object):
+    """
+    Context for locking a directory.
+    """
+    def __init__(self, lockfile):
+        self.lockfile = lockfile
+    def __enter__(self):
+        try:
+            os.open(self.lockfile, os.O_CREAT | os.O_EXCL)
+        except OSError as e:
+            if e.errno == errno.EEXIST:
+                raise DirectoryLockedError(os.getcwd())
+            elif e.errno == errno.EACCES:
+                raise command.PermissionsError(os.getcwd())
+            raise
+    def __exit__(self, *args):
+        try:
+            os.unlink(self.lockfile)
+        except OSError:
+            pass
+
 def chdir(dir):
     """
     Changes a directory, but has special exceptions for certain
@@ -293,3 +314,14 @@ class PermissionsError(IOError):
 
 class NoSuchDirectoryError(IOError):
     errno = errno.ENOENT
+
+class DirectoryLockedError(wizard.Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Could not acquire lock on directory.  Maybe there is
+another migration process running?
+"""
+