import tempfile
import itertools
-from wizard import app, command, deploy, shell, util
+from wizard import app, command, deploy, scripts, shell, util
+
+kib_buffer = 1024 * 30 # 30 MiB we will always leave available
def main(argv, baton):
options, args = parse_args(argv, baton)
sys.stderr.write("Traceback:\n (n/a)\nAlreadyUpgraded\n")
sys.exit(1)
logging.info("Upgrading %s" % os.getcwd())
+ kib_usage, kib_limit = scripts.get_quota_usage_and_limit()
+ if kib_limit is not None and (kib_limit - kib_usage) < kib_buffer: # 10 mebibytes
+ raise QuotaTooLow
if not options.dry_run:
perform_pre_commit(sh)
# If /dev/shm exists, it's a tmpfs and we can use it
open(".git/WIZARD_REPO", "w").write(repo)
open(".git/WIZARD_UPGRADE_VERSION", "w").write(version)
open(".git/WIZARD_PARENTS", "w").write("%s\n%s" % (user_commit, next_commit))
- if options.log_file: open(".git/WIZARD_LOG_FILE", "w").write(options.log_file)
- perform_merge(sh, repo, d, version, use_shm)
+ open(".git/WIZARD_SIZE", "w").write(str(scripts.get_disk_usage()))
+ if options.log_file:
+ open(".git/WIZARD_LOG_FILE", "w").write(options.log_file)
+ perform_merge(sh, repo, d, version, use_shm, kib_limit and kib_limit - kib_usage or None)
# variables: version, user_commit, next_commit, temp_wc_dir
with util.ChangeDirectory(temp_wc_dir):
try:
if options.dry_run:
logging.info("Dry run, bailing. See results at %s" % temp_wc_dir)
return
- # perform database backup
+ # Ok, now we have to do a crazy complicated dance to see if we're
+ # going to have enough quota to finish what we need
+ pre_size = int(open(os.path.join(temp_wc_dir, ".git/WIZARD_SIZE"), "r").read())
+ post_size = scripts.get_disk_usage(temp_wc_dir)
+ kib_usage, kib_limit = scripts.get_quota_usage_and_limit()
backup = d.backup(options)
+ if kib_limit is not None and (kib_limit - kib_usage) - (post_size - pre_size) / 1024 < kib_buffer:
+ shutil.rmtree(os.path.join(".scripts/backups", sh.eval("wizard", "restore").splitlines()[0]))
+ raise QuotaTooLow
# XXX: frob .htaccess to make site inaccessible
with util.IgnoreKeyboardInterrupts():
with util.LockDirectory(".scripts-upgrade-lock"):
raise
except deploy.WebVerificationError as e:
logging.warning("Web verification failed: rolling back")
- logging.info("Web page that was output was:\n\n%s" % e.contents)
perform_restore(d, backup)
- raise app.UpgradeVerificationFailure("Upgrade caused website to become inaccessible; site was rolled back")
+ raise app.UpgradeVerificationFailure(e.contents)
# XXX: frob .htaccess to make site accessible
# to do this, check if .htaccess changed, first. Upgrade
# process might have frobbed it. Don't be
dir = "/dev/shm/wizard"
if not os.path.exists(dir):
os.mkdir(dir)
+ os.chmod(dir, 0o777)
else:
dir = None
temp_dir = tempfile.mkdtemp(prefix="wizard", dir=dir)
sh.call("git", "clone", "-q", "--shared", ".", temp_wc_dir)
return temp_dir, temp_wc_dir
-def perform_merge(sh, repo, d, version, use_shm):
+def perform_merge(sh, repo, d, version, use_shm, kib_avail):
+ # Note: avail_quota == None means unlimited
# naive merge algorithm:
# sh.call("git", "merge", "-m", message, "scripts/master")
# crazy merge algorithm:
user_virtual_commit = sh.eval("git", "commit-tree", user_tree,
"-p", base_virtual_commit, input="", log=True)
sh.call("git", "checkout", user_virtual_commit, "--")
+ d.application.prepareMerge(os.getcwd())
+ sh.call("git", "commit", "--amend", "-a", "-m", "amendment")
try:
sh.call("git", "merge", next_virtual_commit)
except shell.CallError as e:
- conflicts = e.stderr.count("CONFLICT") # not perfect, if there is a file named CONFLICT
+ conflicts = e.stdout.count("CONFLICT") # not perfect, if there is a file named CONFLICT
logging.info("Merge failed with these messages:\n\n" + e.stderr)
# Run the application's specific merge resolution algorithms
# and see if we can salvage it
shutil.move(curdir, newdir)
shutil.rmtree(os.path.dirname(curdir))
curdir = os.path.join(newdir, "repo")
+ os.chdir(curdir)
print "%d %s" % (conflicts, curdir)
raise MergeFailed
"""Base exception for all exceptions raised by upgrade"""
pass
+class QuotaTooLow(Error):
+ def __str__(self):
+ return """
+
+ERROR: The locker quota was too low to complete the autoinstall
+upgrade.
+"""
+
class AlreadyUpgraded(Error):
def __str__(self):
return """