X-Git-Url: https://scripts.mit.edu/gitweb/wizard.git/blobdiff_plain/83de53cbb792a7998334e4ff71741ef5411a43a2..58fee6280427d7af74048b6bce792da2951b077c:/wizard/command/migrate.py diff --git a/wizard/command/migrate.py b/wizard/command/migrate.py index b159427..d3dce85 100644 --- a/wizard/command/migrate.py +++ b/wizard/command/migrate.py @@ -1,70 +1,74 @@ import os -import itertools +import os.path import shutil import logging -import errno -import sys -from wizard import command, deploy, shell, util +from wizard import app, command, deploy, shell, util def main(argv, baton): options, args = parse_args(argv, baton) - dir = args[0] - - shell.drop_priviledges(dir, options) - + dir = os.path.abspath(args[0]) if args else os.getcwd() + shell.drop_priviledges(dir, options.log_file) util.chdir(dir) + sh = shell.Shell(options.dry_run) logging.info("Migrating %s" % dir) logging.debug("uid is %d" % os.getuid()) - deployment = deploy.Deployment(".") - - # deal with old-style migration, remove this later - if os.path.isfile(".scripts/old-version") and not os.path.isfile(".scripts-version"): - os.rename(".scripts/old-version", ".scripts-version") + deployment = deploy.ProductionCopy(".") os.unsetenv("GIT_DIR") # prevent some perverse errors try: deployment.verify() raise AlreadyMigratedError(deployment.location) + except deploy.NotAutoinstallError: + # Previously, this was a fatal error, but now let's try + # a little harder. + # XXX: The user still has to tell us what application ; a more + # user friendly thing to do is figure it out automatically + if not options.force_app: + raise + # actual version number will get overwritten shortly + deployment.setAppVersion(app.ApplicationVersion.make(options.force_app, "unknown")) except deploy.NotMigratedError: + # LEGACY pass except (deploy.CorruptedAutoinstallError, AlreadyMigratedError): - if options.force: - perform_force(options) - else: + if not options.force: raise - deployment.verifyTag(options.srv_path) - if options.force_version: - version = deployment.application.makeVersion(options.force_version) + deployment.setAppVersion(deployment.application.makeVersion(options.force_version)) else: - deployment.verifyVersion() - version = deployment.app_version - repo = version.application.repository(options.srv_path) - tag = version.scripts_tag + try: + deployment.verifyVersion() + except deploy.VersionMismatchError as e: + # well, we'll use that then + deployment.setAppVersion(deployment.application.makeVersion(str(e.real_version))) - # XXX: turn this into a context + deployment.verifyTag(options.srv_path) + + repo = deployment.application.repository(options.srv_path) + tag = deployment.app_version.wizard_tag try: + sh.call("git", "--git-dir=%s" % repo, "rev-parse", tag) + except shell.CallError: + raise UnsupportedVersion(deployment.version) + + with util.LockDirectory(".wizard-migrate-lock"): 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 - make_repository(sh, options, repo, tag) - check_variables(deployment, options) - finally: - try: - os.unlink(".scripts-migrate-lock") - except OSError: - pass + if options.force: + perform_force(options) + make_repository(sh, options, repo, tag) + check_variables(deployment, options) + except KeyboardInterrupt: + # revert it; barring zany race conditions this is safe + if os.path.exists(".wizard"): + shutil.rmtree(".wizard") + if os.path.exists(".git"): + shutil.rmtree(".git") def parse_args(argv, baton): usage = """usage: %prog migrate [ARGS] DIR @@ -73,49 +77,38 @@ Migrates a directory to our Git-based autoinstall format. Performs basic sanity checking and intelligently determines what repository and tag to use. -This command is meant to be run as the owner of the install -it is upgrading (see the scripts AFS kernel patch). Do -NOT run this command as root.""" +This command is meant to be run as the owner of the install it is +upgrading . Do NOT run this command as root.""" parser = command.WizardOptionParser(usage) baton.push(parser, "srv_path") parser.add_option("--dry-run", dest="dry_run", action="store_true", default=False, help="Prints would would be run without changing anything") parser.add_option("--force", "-f", dest="force", action="store_true", - default=False, help="If .git or .scripts directory already exists," + default=False, help="If .git or .wizard directory already exists, " "delete them and migrate") parser.add_option("--force-version", dest="force_version", - default=None, help="If .scripts-version tells lies, explicitly specify" + default=None, help="If .scripts-version is corrupted or non-existent, explicitly specify " "a version to migrate to.") + parser.add_option("--force-app", dest="force_app", + default=None, help="If .scripts-version is corrupted or non-existent, explicitly specify " + "an application to migrate to.") options, args = parser.parse_all(argv) if len(args) > 1: parser.error("too many arguments") - elif not args: - parser.error("must specify directory") return (options, args) def perform_force(options): has_git = os.path.isdir(".git") - has_scripts = os.path.isdir(".scripts") + has_wizard = os.path.isdir(".wizard") if has_git: logging.warning("Force removing .git directory") - if not options.dry_run: backup = safe_unlink(".git") + if not options.dry_run: backup = util.safe_unlink(".git") logging.info(".git backed up to %s" % backup) - if has_scripts: - logging.warning("Force removing .scripts directory") - if not options.dry_run: backup = safe_unlink(".scripts") - logging.info(".scripts backed up to %s" % backup) - -def safe_unlink(file): - """Moves a file to a backup location.""" - prefix = "%s.bak" % file - name = None - for i in itertools.count(): - name = "%s.%d" % (prefix, i) - if not os.path.exists(name): - break - os.rename(file, name) - return name + if has_wizard: + logging.warning("Force removing .wizard directory") + if not options.dry_run: backup = util.safe_unlink(".wizard") + logging.info(".wizard backed up to %s" % backup) def make_repository(sh, options, repo, tag): sh.call("git", "init") # create repository @@ -132,7 +125,7 @@ def make_repository(sh, options, repo, tag): else: logging.info("# create %s containing \"%s\"" % (file, data)) logging.info('# create .htaccess containing "Deny from all"') - # configure our remote (this is merely for convenience; wizard scripts + # configure our remote (this is merely for convenience; wizard # will not rely on this) sh.call("git", "remote", "add", "origin", repo) # configure what would normally be set up on a 'git clone' for consistency @@ -142,11 +135,11 @@ def make_repository(sh, options, repo, tag): sh.call("git", "fetch", "origin") # soft reset to our tag sh.call("git", "reset", tag, "--") - # checkout the .scripts directory - sh.call("git", "checkout", ".scripts") + # initialize the .wizard directory + util.init_wizard_dir() logging.info("Diffstat:\n" + sh.eval("git", "diff", "--stat")) # commit user local changes - message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer()) + message = "Autoinstall migration.\n\n%s" % util.get_git_footer() util.set_git_env() try: message += "\nMigrated-by: " + util.get_operator_git() @@ -168,23 +161,25 @@ class Error(command.Error): pass class AlreadyMigratedError(Error): + quiet = True def __init__(self, dir): self.dir = dir def __str__(self): return """ -ERROR: Directory already contains a .git and -.scripts directory. If you force this migration, -both of these directories will be removed. +This autoinstall is already migrated; move along, nothing to +see here. (If you really want to, you can force a re-migration +with --force, but this will blow away the existing .git and +.scripts directories (i.e. your history and Wizard configuration).) """ -class DirectoryLockedError(Error): - def __init__(self, dir): - self.dir = dir +class UnsupportedVersion(Error): + def __init__(self, version): + self.version = version def __str__(self): return """ -ERROR: Could not acquire lock on directory. Maybe there is -another migration process running? -""" - +ERROR: This autoinstall is presently on %s, which is unsupported by +Wizard. Please manually upgrade it to one that is supported, +and then retry the migration; usually the latest version is supported. +""" % self.version