+import logging
+import sys
+import optparse
+
+def makeLogger(options):
+ logger = logging.getLogger("main")
+ logger.setLevel(logging.INFO)
+ stdout = logging.StreamHandler(sys.stdout)
+ logger.addHandler(stdout)
+ if options.verbose:
+ logger.verbose = True
+ stdout.setLevel(logging.INFO)
+ else:
+ stdout.setLevel(logging.ERROR)
+ return logger
+
+class UserException(Exception):
+ """User friendly exceptions inherit from here"""
+ pass
+
+class NullLogHandler(logging.Handler):
+ """Log handler that doesn't do anything"""
+ def emit(self, record):
+ pass
+
+class WizardOptionParser(optparse.OptionParser):
+ """Configures some default user-level options"""
+ def __init__(self, *args, **kwargs):
+ optparse.OptionParser.__init__(self, *args, **kwargs)
+ self.add_option("-v", "--verbose", dest="verbose", action="store_true",
+ default=False, help="Turns on verbose output")
+ def parse_all(self, argv, logger):
+ options, numeric_args = self.parse_args(argv)
+ return options, numeric_args, makeLogger(options)
import sys
import os
import shutil
+import logging.handlers
+from wizard import *
import wizard.deploy as wd
import wizard.shell as sh
-def main(argv, global_options):
+class PermissionsError(UserException):
+ 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(UserException):
+ def __init__(self, dir):
+ self.dir = dir
+ def __str__(self):
+ return """
+
+ERROR: No such directory... check your typing
+"""
+
+class AlreadyMigratedError(UserException):
+ def __init__(self, dir):
+ self.dir = dir
+ def __str__(self):
+ return """
+
+ERROR: Directory already contains a .git directory.
+Did you already migrate it?
+"""
+
+class NotAutoinstallError(UserException):
+ 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 NoRepositoryError(UserException):
+ def __init__(self, app):
+ self.app = app
+ def __str__(self):
+ return """
+
+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
+
+class NoTagError(UserException):
+ 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)
+
+def migrate(argv, global_options, logger = None):
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 = optparse.OptionParser(usage)
- parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
- default=False, help="Print all commands and outputs")
+ parser = WizardOptionParser(usage)
parser.add_option("--dry-run", dest="dry_run", action="store_true",
default=False, help="Prints would would be run without changing anything")
- options, args = parser.parse_args(argv)
+ options, args, logger = parser.parse_all(argv, logger)
if len(args) > 1:
parser.error("too many arguments")
elif not args:
parser.error("must specify directory")
dir = args[0]
- print "Changing working directory to autoinstall directory"
try:
os.chdir(dir)
except OSError as e:
if e.errno == 13:
- print
- print "ERROR: You don't have permissions to access this directory."
- print "Do you have tickets for AFS with your root instance, and"
- print "is your root instance on scripts-security-upd?"
- print
- print "You can check by running the commands 'klist' and"
- print "'blanche scripts-security-upd'. We recommend getting"
- print "root tickets using Nelson Elhage's krbroot script"
- print "at /mit/nelhage/Public/krbroot (for which you run"
- print "'krbroot shell' and then 'aklog')."
- raise SystemExit(-1)
+ raise PermissionsError(dir)
elif e.errno == 2:
- print
- print "ERROR: No such directory... check your typing"
- raise SystemExit(-1)
+ raise NoSuchDirectoryError(dir)
else: raise e
+ if os.path.isdir(".git"):
+ raise AlreadyMigratedError(dir)
try:
deploy = wd.Deployment.fromDir(".")
version = deploy.getAppVersion()
except IOError as e:
if e.errno == 2:
- print
- print "ERROR: Could not find .scripts-version file. Are you sure"
- print "this is an autoinstalled application?"
- raise SystemExit(-1)
+ 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):
- print
- print "ERROR: Could not find repository for this application. Have"
- print "you converted the repository over? Is the name %s" % app
- print "the same as the the name of the foo.git folder?"
- raise SystemExit(-1)
+ raise NoRepositoryError(app)
# begin the command line process
- shell = sh.Shell(options.verbose, options.dry_run)
+ shell = sh.Shell(logger, options.dry_run)
# 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.
tag = "v%s-scripts" % version.version
shell.call("git", "--git-dir", repo, "rev-parse", tag)
except sh.CalledProcessError:
- print
- print "ERROR: Could not find tag v%s-scripts for" % version.version
- print "this application's version. Double check and make sure"
- print "the repository was prepared with all necessary tags!"
- raise SystemExit(-1)
+ raise NoTagError(version)
did_git_init = False
did_git_checkout_scripts = False
try:
did_git_checkout_scripts = True
# XXX: setup .scripts/version???
# for verbose purposes, give us a git status and git diff
+ raise NotImplementedError
if options.verbose:
shell.call("git", "status")
shell.call("git", "diff")
except:
- print
- print "ERROR: Exception detected! Rolling back..."
+ logger.error("ERROR: Exception detected! Rolling back...")
if did_git_init:
- print "Deleting .git directory"
shell.call("rm", "-Rf", ".git")
if did_git_checkout_scripts:
- print "Deleting .scripts directory"
shell.call("rm", "-Rf", ".scripts")
raise
import subprocess
-from subprocess import CalledProcessError
+from subprocess import CalledProcessError, PIPE, STDOUT
import sys
class Shell(object):
"""An advanced shell, with the ability to do dry-run and log commands"""
- def __init__(self, verbose = False, dry = False):
- """ `verbose` Whether or not to print the command and outputs
+ def __init__(self, logger = False, dry = False):
+ """ `logger` The logger
`dry` Whether or not to not run any commands, and just print"""
- self.verbose = verbose
+ self.logger = logger
self.dry = dry
def call(self, *args):
- if self.dry or self.verbose:
- print "$ " + ' '.join(args)
+ if self.dry or self.logger:
+ self.logger.info("$ " + ' '.join(args))
if self.dry: return
proc = None
- if self.verbose:
- proc = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr)
+ if self.logger:
+ if hasattr(self.logger, "verbose"):
+ proc = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr)
+ else:
+ proc = subprocess.Popen(args, stdout=PIPE, stderr=STDOUT)
else:
proc = subprocess.Popen(args)
- proc.communicate()
+ stdout, _ = proc.communicate()
+ if self.logger and stdout: self.logger.info(stdout)
if proc.returncode:
raise CalledProcessError(proc.returncode, args)