def load_config(args, kind): config_file = { "global": os.path.join(os.path.expanduser("~"), ".gondor"), "local": os.path.abspath(os.path.join(os.curdir, "gondor.yml")), }[kind] try: return yaml.load(open(config_file, "rb")) except IOError: error("unable to find configuration file (looked for %s)\n" % config_file) except yaml.parser.ParserError: if kind == "global": c = ConfigParser.RawConfigParser() try: c.read(config_file) except Exception: # ignore any exceptions while reading config pass else: if args.verbose > 1: warn("upgrade %s to YAML\n" % config_file) return { "auth": { "username": config_value(c, "auth", "username"), "key": config_value(c, "auth", "key"), } } error("unable to parse %s\n" % config_file)
def cmd_manage(args, env, config): instance_label = args.label[0] operation = args.operation[0] opargs = args.opargs if operation == "database:load" and not args.yes: out("This command will destroy all data in the database for %s\n" % instance_label) answer = raw_input("Are you sure you want to continue? [y/n]: ") if answer != "y": sys.exit(1) url = "%s/instance/manage/" % config["gondor.endpoint"] params = { "version": __version__, "site_key": config["gondor.site_key"], "instance_label": instance_label, "operation": operation, } handlers = [ http.MultipartPostHandler, ] if operation in ["database:load"]: if opargs: filename = os.path.abspath(os.path.expanduser(opargs[0])) try: fp = open(filename, "rb") except IOError: error("unable to open %s\n" % filename) out("Compressing file... ") fd, tmp = tempfile.mkstemp() fpc = gzip.open(tmp, "wb") try: while True: chunk = fp.read(8192) if not chunk: break fpc.write(chunk) finally: fpc.close() out("[ok]\n") params["stdin"] = open(tmp, "rb") pb = ProgressBar(0, 100, 77) out("Pushing file to Gondor... \n") handlers.extend([ http.UploadProgressHandler(pb, ssl=True), http.UploadProgressHandler(pb, ssl=False) ]) else: error("%s takes one argument.\n" % operation) params = params.items() for oparg in opargs: params.append(("arg", oparg)) try: response = make_api_call(config, url, params, extra_handlers=handlers) except urllib2.HTTPError, e: api_error(e)
def cmd_deploy(args, env, config): label = args.label[0] commit = args.commit[0] tar_path, tarball_path = None, None try: if config["gondor.vcs"] == "git": try: git = utils.find_command("git") except utils.BadCommand, e: error(e.args[0]) check, sha = utils.run_proc([git, "rev-parse", commit]) if check != 0: error("could not map '%s' to a SHA\n" % commit) if commit == "HEAD": commit = sha tar_path = os.path.abspath(os.path.join(env["repo_root"], "%s-%s.tar" % (label, sha))) cmd = [git, "archive", "--format=tar", commit, "-o", tar_path] elif config["gondor.vcs"] == "hg": try: hg = utils.find_command("hg") except utils.BadCommand, e: error(e.args[0])
def main(): parser = argparse.ArgumentParser(prog="gondor") parser.add_argument("--version", action="version", version="%%(prog)s %s" % __version__) parser.add_argument("--verbose", "-v", action="count", default=1) command_parsers = parser.add_subparsers(dest="command") # cmd: init parser_init = command_parsers.add_parser("init") parser_init.add_argument("--upgrade", action="store_true") parser_init.add_argument("site_key", nargs="?") # cmd: create parser_create = command_parsers.add_parser("create") parser_create.add_argument("--kind") parser_create.add_argument("label", nargs=1) # cmd: deploy parser_deploy = command_parsers.add_parser("deploy") parser_deploy.add_argument("--no-on-deploy", action="store_true") parser_deploy.add_argument("label", nargs=1) parser_deploy.add_argument("commit", nargs=1) # cmd: sqldump parser_sqldump = command_parsers.add_parser("sqldump") parser_sqldump.add_argument("label", nargs=1) # cmd: run parser_run = command_parsers.add_parser("run") parser_run.add_argument("--detached", action="store_true", help="run process in detached (output is sent to logs)" ) parser_run.add_argument("instance_label", nargs=1) parser_run.add_argument("command_", nargs=argparse.REMAINDER) # cmd: delete parser_delete = command_parsers.add_parser("delete") parser_delete.add_argument("label", nargs=1) # cmd: list parser_list = command_parsers.add_parser("list") # cmd: manage # example: gondor manage primary database:reset # example: gondor manage dev database:copy primary parser_manage = command_parsers.add_parser("manage") parser_manage.add_argument("label", nargs=1) parser_manage.add_argument("operation", nargs=1) parser_manage.add_argument("--yes", action="store_true", help="automatically answer yes to prompts" ) parser_manage.add_argument("opargs", nargs="*") # cmd: open # example: gondor open primary parser_open = command_parsers.add_parser("open") parser_open.add_argument("label", nargs=1) # cmd: dashboard # example: gondor dashboard primary parser_dashboard = command_parsers.add_parser("dashboard") parser_dashboard.add_argument("label", nargs="?") # cmd: env # example: gondor env / gondor env primary / gondor env KEY / gondor env primary KEY parser_env = command_parsers.add_parser("env") parser_env.add_argument("--scoped", action="store_true") parser_env.add_argument("bits", nargs="*") # cmd: env:set # example: gondor env:set KEY=value / gondor env primary KEY=value parser_env_set = command_parsers.add_parser("env:set") parser_env_set.add_argument("bits", nargs="*") args = parser.parse_args() # config / env global_config = load_config(args, "global") config = { "auth.username": global_config.get("auth", {}).get("username"), "auth.key": global_config.get("auth", {}).get("key"), } env = {} if args.command in ["sqldump"]: out = err else: out = globals()["out"] if args.command != "init": config_file = "gondor.yml" try: env["project_root"] = utils.find_nearest(os.getcwd(), config_file) except OSError: error("unable to find %s configuration file.\n" % config_file) if args.verbose > 1: out("Reading configuration... ") local_config = load_config(args, "local") if args.verbose > 1: out("[ok]\n") config.update({ "auth.username": local_config.get("auth", {}).get("username", config["auth.username"]), "auth.key": local_config.get("auth", {}).get("key", config["auth.key"]), "gondor.site_key": local_config.get("key"), "gondor.endpoint": local_config.get("endpoint", DEFAULT_ENDPOINT), "gondor.vcs": local_config.get("vcs"), "app": { "requirements_file": local_config.get("requirements_file"), "framework": local_config.get("framework"), "on_deploy": local_config.get("on_deploy", []), "static_urls": list(itertools.chain(*[ [(u, c) for u, c in su.iteritems()] for su in local_config.get("static_urls", []) ])), "wsgi_entry_point": local_config.get("wsgi", {}).get("entry_point"), "gunicorn_worker_class": local_config.get("wsgi", {}).get("gunicorn", {}).get("worker_class"), "settings_module": local_config.get("django", {}).get("settings_module"), "managepy": local_config.get("django", {}).get("managepy"), "local_settings": local_config.get("django", {}).get("local_settings"), "env": local_config.get("env", {}), } }) # allow some values to be overriden from os.environ config["auth.username"] = os.environ.get("GONDOR_AUTH_USERNAME", config["auth.username"]) config["auth.key"] = os.environ.get("GONDOR_AUTH_KEY", config["auth.key"]) config["gondor.site_key"] = os.environ.get("GONDOR_SITE_KEY", config["gondor.site_key"]) try: vcs_dir = {"git": ".git", "hg": ".hg"}[config["gondor.vcs"]] except KeyError: error("'%s' is not a valid version control system for Gondor\n" % config["gondor.vcs"]) try: env["repo_root"] = utils.find_nearest(os.getcwd(), vcs_dir) except OSError: error("unable to find a %s directory.\n" % vcs_dir) if config["auth.username"] is None or config["auth.key"] is None: error( "you must provide a username and API key in %s or set it in " "the environment.\n" % os.path.expanduser("~/.gondor") ) if config["gondor.site_key"] is None: error("no site key found in configuration or environment.\n") { "init": cmd_init, "create": cmd_create, "deploy": cmd_deploy, "sqldump": cmd_sqldump, "run": cmd_run, "delete": cmd_delete, "list": cmd_list, "manage": cmd_manage, "open": cmd_open, "dashboard": cmd_dashboard, "env": cmd_env, "env:set": cmd_env_set, }[args.command](args, env, config) return 0
def cmd_init(args, env, config): config_file = "gondor.yml" ctx = dict(config_file=config_file) if args.upgrade: gondor_dir = utils.find_nearest(os.getcwd(), ".gondor") legacy_config = ConfigParser.RawConfigParser() legacy_config.read(os.path.abspath(os.path.join(gondor_dir, ".gondor", "config"))) ctx.update({ "site_key": config_value(legacy_config, "gondor", "site_key"), "vcs": config_value(legacy_config, "gondor", "vcs"), "requirements_file": config_value(legacy_config, "app", "requirements_file"), "wsgi_entry_point": config_value(legacy_config, "app", "wsgi_entry_point"), "framework": "django", "gunicorn_worker_class": "eventlet", }) on_deploy, static_urls = [], [] migrations = config_value(legacy_config, "app", "migrations") if migrations: migrations = migrations.strip().lower() if migrations == "none": on_deploy.append(" - manage.py syncdb --noinput") if migrations == "nashvegas": on_deploy.append(" - manage.py upgradedb --execute") if migrations == "south": on_deploy.append(" - manage.py syncdb --noinput") on_deploy.append(" - manage.py migrate --noinput") staticfiles = config_value(legacy_config, "app", "staticfiles") if staticfiles: staticfiles = staticfiles.strip().lower() if staticfiles == "on": on_deploy.append(" - manage.py collectstatic --noinput") compressor = config_value(legacy_config, "app", "compressor") if compressor: compressor = compressor.strip().lower() if compressor == "on": on_deploy.append(" - manage.py compress") site_media_url = config_value(legacy_config, "app", "site_media_url") managepy = config_value(legacy_config, "app", "managepy") if not managepy: managepy = "manage.py" if site_media_url: static_urls.extend([" - %s:" % site_media_url, " root: site_media/"]) extra_config_file_data = """ django: # The location of your manage.py. Gondor uses this as an entry point for # management commands. This path is relative to your project root (the # directory %(config_file)s lives in.) managepy: %(managepy)s """ % { "managepy": managepy, "config_file": config_file, } else: site_key = args.site_key if len(site_key) < 11: error("The site key given is too short.\n") ctx["wsgi_entry_point"] = "wsgi:application" ctx["requirements_file"] = "requirements.txt" on_deploy = [] static_urls = [" - /site_media:", " root: site_media/"] try: utils.find_nearest(os.getcwd(), ".git") except OSError: try: utils.find_nearest(os.getcwd(), ".hg") except OSError: error("unable to find a supported version control directory. Looked for .git and .hg.\n") else: vcs = "hg" else: vcs = "git" extra_config_file_data = "" ctx.update({ "site_key": site_key, "vcs": vcs, "framework": "wsgi", "requirements_file": "requirements.txt", "wsgi_entry_point": "wsgi:application", "gunicorn_worker_class": "sync", }) if not on_deploy: ctx["on_deploy"] = "# on_deploy:\n# - manage.py syncdb --noinput\n# - manage.py collectstatic --noinput" else: ctx["on_deploy"] = "\n".join(["on_deploy:"] + on_deploy) ctx["static_urls"] = "\n".join(["static_urls:"] + static_urls) if not os.path.exists(config_file): config_file_data = """# The key associated to your site. key: %(site_key)s # Version control system used locally for your project. vcs: %(vcs)s # Framework to use on Gondor. framework: %(framework)s # This path is relative to your project root (the directory %(config_file)s lives in.) requirements_file: %(requirements_file)s # Commands to be executed during deployment. These can handle migrations or # moving static files into place. Accepts same parameters as gondor run. %(on_deploy)s # URLs which should be served by Gondor mapping to a filesystem location # relative to your writable storage area. %(static_urls)s wsgi: # The WSGI entry point of your application in two parts separated by a # colon. Example: # # wsgi:application # # wsgi = the Python module which should be importable # application = the callable in the Python module entry_point: %(wsgi_entry_point)s # Options for gunicorn which runs your WSGI project. gunicorn: # The worker class used to run gunicorn (possible values include: # sync, eventlet and gevent) worker_class: %(gunicorn_worker_class)s """ % ctx out("Writing configuration (%s)... " % config_file) with open(config_file, "wb") as cf: cf.write(config_file_data + extra_config_file_data) out("[ok]\n") if args.upgrade: out("\nYour configuration file has been upgraded. New configuration is located\n") out("in %s. Make sure you check this file before continuing then add and\n" % config_file) out("commit it to your VCS.\n") else: out("\nYou are now ready to deploy your project to Gondor. You might want to first\n") out("check %s (in this directory) for correct values for your\n" % config_file) out("application. Once you are ready, run:\n\n") out(" gondor deploy primary %s\n" % {"git": "master", "hg": "default"}[vcs]) else: out("Detected existing %s. Not overriding.\n" % config_file)
response = make_api_call(config, url, urllib.urlencode(params)) except urllib2.HTTPError, e: api_error(e) data = json.loads(response.read()) if data["status"] == "error": message = "error" elif data["status"] == "success": message = "ok" else: message = "unknown" out("\r%s[%s] \n" % (text, message)) if data["status"] == "success": out("\nRun: gondor deploy %s %s" % (label, {"git": "HEAD", "hg": "tip"}[config["gondor.vcs"]])) out("\nVisit: %s\n" % data["url"]) else: error("%s\n" % data["message"]) def cmd_deploy(args, env, config): label = args.label[0] commit = args.commit[0] tar_path, tarball_path = None, None try: if config["gondor.vcs"] == "git": try: git = utils.find_command("git") except utils.BadCommand, e: error(e.args[0]) check, sha = utils.run_proc([git, "rev-parse", commit])