Пример #1
0
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)
Пример #2
0
def cmd_delete(args, env, config):
    
    instance_label = args.label[0]
    
    text = "ARE YOU SURE YOU WANT TO DELETE THIS INSTANCE? [Y/N] "
    out(text)
    user_input = raw_input()
    if user_input != "Y":
        out("Exiting without deleting the instance.\n")
        sys.exit(0)
    text = "Deleting... "
    
    url = "%s/instance/delete/" % config["gondor.endpoint"]
    params = {
        "version": __version__,
        "site_key": config["gondor.site_key"],
        "instance_label": instance_label,
    }
    try:
        response = make_api_call(config, url, urllib.urlencode(params))
    except urllib2.HTTPError, e:
        api_error(e)
Пример #3
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
Пример #4
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)
Пример #5
0
        "label": label,
        "kind": kind,
        "project_root": os.path.basename(env["project_root"]),
    }
    try:
        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":