]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/command/migrate.py
Fix wizard -> wizard_bin bug, fix dry run, create .scripts/variables
[wizard.git] / wizard / command / migrate.py
index 4136f3fa84238a86e3624f5b8d66775361bed24d..fd39dbe1cbd78c8a02b39ae3f0d349075743e98a 100644 (file)
-import optparse
-import sys
 import os
 import shutil
-import logging.handlers
+import logging
 import errno
+import sys
 
-from wizard import deploy
-from wizard import shell
-from wizard.command import _base
-
-class Error(_base.Error):
-    """Base exception for all exceptions raised by migrate"""
-    pass
-
-class PermissionsError(Error):
-    def __init__(self, dir):
-        self.dir = dir
-    def __str__(self):
-        return """
-
-ERROR: You don't have permissions to access this directory.
-Do you have tickets for AFS with your root instance, and
-is your root instance on scripts-security-upd?
-
-You can check by running the commands 'klist' and
-'blanche scripts-security-upd'.  We recommend getting
-root tickets using Nelson Elhage's krbroot script
-at /mit/nelhage/Public/krbroot (for which you run
-'krbroot shell' and then 'aklog').
-"""
-
-class NoSuchDirectoryError(Error):
-    def __init__(self, dir):
-        self.dir = dir
-    def __str__(self):
-        return """
-
-ERROR: No such directory... check your typing
-"""
+from wizard import command, deploy, shell, util
 
-class AlreadyMigratedError(Error):
-    def __init__(self, dir):
-        self.dir = dir
-    def __str__(self):
-        return """
-
-ERROR: Directory already contains a .git and/or
-.scripts directory.  Did you already migrate it?
-"""
+def main(argv, baton):
+    options, args = parse_args(argv)
+    dir = args[0]
 
-class NotAutoinstallError(Error):
-    def __init__(self, dir):
-        self.dir = dir
-    def __str__(self):
-        return """
+    logging.debug("uid is %d" % os.getuid())
 
-ERROR: Could not find .scripts-version file. Are you sure
-this is an autoinstalled application?
-"""
+    command.chdir(dir)
+    check_if_already_migrated(options)
 
-class NoRepositoryError(Error):
-    def __init__(self, app):
-        self.app = app
-    def __str__(self):
-        return """
+    deployment = make_deployment() # uses chdir
+    version = deployment.app_version
+    repo    = version.application.repository
+    tag     = version.scripts_tag
 
-ERROR: Could not find repository for this application. Have
-you converted the repository over? Is the name %s
-the same as the name of the .git folder?
-""" % self.app
+    os.unsetenv("GIT_DIR") # prevent some perverse errors
 
-class NoTagError(Error):
-    def __init__(self, version):
-        self.version = version
-    def __str__(self):
-        return """
+    sh = shell.Shell(options.dry_run)
+    check_if_tag_exists(sh, repo, tag)
+    make_repository(sh, options, repo, tag)
+    make_variables(deployment, options)
 
-ERROR: Could not find tag v%s-scripts in repository
-for %s.  Double check and make sure
-the repository was prepared with all necessary tags!
-""" % (self.version.version, self.version.application.name)
+    if not options.dry_run:
+        os.rename(".scripts-version", ".scripts/old-version") # archive
+    else:
+        logging.info("mv .scripts-version .scripts/old-version")
 
-def main(argv, global_options, logger = None):
+def parse_args(argv):
     usage = """usage: %prog migrate [ARGS] DIR
 
 Migrates a directory to our Git-based autoinstall format.
 Performs basic sanity checking and intelligently determines
-what repository and tag to use."""
-    parser = _base.WizardOptionParser(usage)
+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."""
+    parser = command.WizardOptionParser(usage)
     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, delete them and migrate")
-    options, args, logger = parser.parse_all(argv, logger)
+    options, args = parser.parse_all(argv)
     if len(args) > 1:
         parser.error("too many arguments")
     elif not args:
         parser.error("must specify directory")
-    dir = args[0]
-    try:
-        os.chdir(dir)
-    except OSError as e:
-        if e.errno == errno.EACCES:
-            raise PermissionsError(dir)
-        elif e.errno == errno.ENOENT:
-            raise NoSuchDirectoryError(dir)
-        else: raise e
+    return (options, args)
+
+def check_if_already_migrated(options):
     if os.path.isdir(".git") or os.path.isdir(".scripts"):
         if not options.force:
             raise AlreadyMigratedError(dir)
         else:
             if os.path.isdir(".git"):
-                logger.warning("Force removing .git directory")
+                logging.warning("Force removing .git directory")
                 if not options.dry_run: shutil.rmtree(".git")
             if os.path.isdir(".scripts"):
-                logger.warning("Force removing .scripts directory")
+                logging.warning("Force removing .scripts directory")
                 if not options.dry_run: shutil.rmtree(".scripts")
+
+def make_deployment():
     try:
-        d = deploy.Deployment.fromDir(".")
-        version = d.getAppVersion()
+        return deploy.Deployment(".")
     except IOError as e:
         if e.errno == errno.ENOENT:
             raise NotAutoinstallError(dir)
         else: raise e
-    # calculate the repository we'll be pulling out of
-    application = version.application
-    app = application.name
-    repo = os.path.join("/afs/athena.mit.edu/contrib/scripts/wizard/srv", app + ".git")
-    if not os.path.isdir(repo):
-        raise NoRepositoryError(app)
-    # begin the command line process
-    sh = shell.Shell(logger, options.dry_run)
+
+def check_if_tag_exists(sh, repo, tag):
     # check if the version we're trying to convert exists. We assume
     # a convention here, namely, v1.2.3-scripts is what we want. If
     # you broke the convention... shame on you.
     try:
-        tag = "v%s-scripts" % version.version
         sh.call("git", "--git-dir", repo, "rev-parse", tag)
     except shell.CallError:
         raise NoTagError(version)
-    did_git_init = False
-    did_git_checkout_scripts = False
+
+def make_repository(sh, options, repo, tag):
+    sh.call("git", "init") # create repository
+    # configure our alternates (to save space and make this quick)
+    data = os.path.join(repo, "objects")
+    file = ".git/objects/info/alternates"
+    if not options.dry_run:
+        alternates = open(file, "w")
+        alternates.write(data)
+        alternates.close()
+        htaccess = open(".git/.htaccess", "w")
+        htaccess.write("Deny from all\n")
+        htaccess.close()
+    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
+    # 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
+    sh.call("git", "config", "branch.master.remote", "origin")
+    sh.call("git", "config", "branch.master.merge", "refs/heads/master")
+    # perform the initial fetch
+    sh.call("git", "fetch", "origin")
+    # soft reset to our tag
+    sh.call("git", "reset", tag, "--")
+    # checkout the .scripts directory
+    sh.call("git", "checkout", ".scripts")
+    # commit user local changes
+    message = "Autoinstall migration of %s locker.\n\n%s" % (util.get_dir_owner(), util.get_git_footer())
+    util.set_git_env()
     try:
-        # create repository
-        sh.call("git", "--git-dir=.git", "init")
-        did_git_init = True
-        # configure our alternates (to save space and make this quick)
-        data = os.path.join(repo, "objects")
-        file = ".git/objects/info/alternates"
-        if not options.dry_run:
-            alternates = open(file, "w")
-            alternates.write(data)
-            alternates.close()
+        message += "\nMigrated-by: " + util.get_operator_git()
+    except util.NoOperatorInfo:
+        pass
+    sh.call("git", "commit", "--allow-empty", "-a", "-m", message)
+    # for verbose purposes, give us a git status and git diff
+    if options.verbose:
+        try:
+            sh.call("git", "status")
+        except shell.CallError:
+            pass
+        try:
+            sh.call("git", "diff")
+        except shell.CallError:
+            pass
+
+def make_variables(d, options):
+    """Make .scripts/variables which contains variables based off of
+    what was regexed out of existing configuration files."""
+    variables = d.extract()
+    if not options.dry_run: f = open(".scripts/variables", "w")
+    for k,v in variables.items():
+        if v is None:
+            # once we get everything on the same version, you should
+            # actually start paying attention to these warnings
+            logging.warning("Variable %s not found" % k)
         else:
-            logger.info("# create %s containing \"%s\"" % (file, data))
-        # configure our remote
-        sh.call("git", "remote", "add", "origin", repo)
-        # configure what would normally be set up on a 'git clone' for consistency
-        sh.call("git", "config", "branch.master.remote", "origin")
-        sh.call("git", "config", "branch.master.merge", "refs/heads/master")
-        # perform the initial fetch
-        sh.call("git", "fetch", "origin")
-        # soft reset to our tag
-        sh.call("git", "reset", tag, "--")
-        # checkout the .scripts directory
-        sh.call("git", "checkout", ".scripts")
-        did_git_checkout_scripts = True
-        # XXX: setup .scripts/version???
-        # for verbose purposes, give us a git status and git diff
-        if options.verbose:
-            try:
-                sh.call("git", "status")
-            except shell.CallError:
-                pass
-            try:
-                sh.call("git", "diff")
-            except shell.CallError:
-                pass
-    except:
-        # this... is pretty bad
-        logger.critical("ERROR: Exception detected! Rolling back...")
-        if did_git_init:
-            sh.call("rm", "-Rf", ".git")
-        if did_git_checkout_scripts:
-            sh.call("rm", "-Rf", ".scripts")
-        raise
+            logging.debug("Variable %s is %s" % (k,v))
+            if not options.dry_run:
+                f.write("%s %s\n" % (k,v))
+            else:
+                logging.info('# write line "%s %s" to .scripts/variables' % (k,v))
+    if not options.dry_run: f.close()
+
+class Error(command.Error):
+    """Base exception for all exceptions raised by migrate"""
+    pass
+
+class AlreadyMigratedError(Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Directory already contains a .git and/or
+.scripts directory.  Did you already migrate it?
+"""
+
+class NotAutoinstallError(Error):
+    def __init__(self, dir):
+        self.dir = dir
+    def __str__(self):
+        return """
+
+ERROR: Could not find .scripts-version file. Are you sure
+this is an autoinstalled application?
+"""
+
+class NoTagError(Error):
+    def __init__(self, version):
+        self.version = version
+    def __str__(self):
+        return """
+
+ERROR: Could not find tag v%s-scripts in repository
+for %s.  Double check and make sure
+the repository was prepared with all necessary tags!
+""" % (self.version.version, self.version.application.name)