def upload(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) public_key_path = kwargs["public_key_path"] if not public_key_path.endswith(".pub"): click.confirm( ("Key {} appears to be a private, not a public key. " "Are you sure you want to continue?").format(public_key_path), abort=True, ) expanded_public_key_path = os.path.expanduser(public_key_path) if not os.path.exists(expanded_public_key_path): raise click.exceptions.ClickException( "File {} does not exist".format(public_key_path)) cnf = config.get_config() stolos_url = kwargs.pop("stolos_url") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] name = kwargs.get("name") if not name: name = platform.node() with open(expanded_public_key_path, "r") as fin: resp = api.keys_create(cnf["user"][_get_hostname(stolos_url)], ssh_public_key=fin.read(), name=name) updated_conf = cnf["user"][_get_hostname(stolos_url)] updated_conf["identity-file"] = os.path.abspath( re.sub(".pub$", "", expanded_public_key_path)) config.update_user_config({"user": {stolos_url: updated_conf}}) if resp.ok: click.echo( "Public key {} uploaded successfully".format(public_key_path))
def _sync(repeat): """ Starts a project sync using Unison. Takes an extra parameter, which makes the synchronization repeat using Unison `-repeat` or not. """ cnf = config.get_config() _config_environ(cnf) identity_file = cnf["user"][cnf["user"]["default-api-server"]].get( "identity-file") if identity_file is None: click.echo( click.style("[WARNING] ", bold=True) + "No public key was found. Your user's default key will be used.") click.echo("To upload a public ssh key, use the following command:") click.secho("\tstolos keys upload [PUBLIC_KEY_PATH]\n", bold=True) home = os.path.expanduser("~") identity_file = os.path.join(home, ".ssh", "id_rsa") args = [] args.insert(0, "-i {}".format(identity_file)) args.insert(0, "-sshargs") if repeat: args.insert(0, "2") args.insert(0, "-repeat") else: args.insert(0, "false") args.insert(0, "-fastcheck") if _is_windows(): args.insert(0, "win") p = subprocess.Popen(["unison"] + args, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin) return p
def sync(repeat): _ensure_stolos_directory() cnf = config.get_config() _config_environ(cnf) click.echo("Syncing...") _sync(repeat).wait() if not repeat: click.echo("Okay.")
def env(**kwargs): _ensure_stolos_directory() cnf = config.get_config() stolos_url = cnf["user"]["default-api-server"] _ensure_logged_in(stolos_url) env_dict = _get_environ(cnf) command = "stolos env" if kwargs["shell"]: command = "stolos env --shell={}".format(kwargs["shell"]) shell.print_env_eval(command, kwargs["shell"], env_dict)
def keys_delete(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) public_key_uuid = kwargs.get("public_key_uuid") cnf = config.get_config() stolos_url = kwargs.pop("stolos_url") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] click.echo('Deleting SSH public key "{}"...'.format(public_key_uuid), nl=False) api.keys_remove(cnf["user"][_get_hostname(stolos_url)], public_key_uuid) click.echo("\t\tOkay.")
def stacks_list(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) cnf = config.get_config() stolos_url = kwargs.get("stolos_url") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] headers = ["Stack name", "Slug", "Description"] stacks = [ (stack["name"], stack["slug"], stack.get("description")) for stack in api.stacks_list(cnf["user"][_get_hostname(stolos_url)]) ] click.echo(tabulate(stacks, headers=headers))
def password(**kwargs): cnf = config.get_config() stolos_url = kwargs.get("stolos_url") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] _ensure_logged_in(stolos_url) api.change_password( cnf["user"][_get_hostname(stolos_url)], kwargs["password"], kwargs["new_password"], ) click.echo("Password successfully updated.")
def _ensure_logged_in(stolos_url=None): """ Ensures the user is logged in, at the given `stolos_url` Stolos server. Raises an exception if not. """ cnf = config.get_config() if "user" not in cnf: raise exceptions.NotLoggedInException() if "default-api-server" not in cnf["user"] and not stolos_url: raise exceptions.NotLoggedInException() stolos_url = stolos_url or cnf["user"]["default-api-server"] if stolos_url not in cnf["user"]: raise exceptions.NotLoggedInException()
def keys_list(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) cnf = config.get_config() stolos_url = kwargs.get("stolos_url") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] algorithm = "md5" if not kwargs["md5"]: algorithm = "sha256" headers = ["UUID", "Name", algorithm.upper()] keys = [(key["uuid"], key["name"], key[algorithm]) for key in api.keys_list(cnf["user"][_get_hostname(stolos_url)])] click.echo(tabulate(keys, headers=headers))
def _config_environ(cnf): """ Configures the environment with any needed environment variables for compose and Unison. Also updates the docker certificates to the latest valid from user config. """ cnf = config.get_config() server = cnf["user"]["default-api-server"] with open(".stolos/cert.pem", "w+") as cert_pem: cert_pem.write(cnf["user"][server].get("cert-pem", "")) os.chmod(".stolos/cert.pem", 0o600) with open(".stolos/key.pem", "w+") as key_pem: key_pem.write(cnf["user"][server].get("key-pem", "")) os.environ.update(_get_environ(cnf))
def connect(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) cnf = config.get_config() stolos_url = kwargs.pop("stolos_url") project_uuid = kwargs.pop("project_uuid") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] click.echo('Connecting to project "{}"...'.format(project_uuid), nl=False) project = api.projects_retrieve(cnf["user"][_get_hostname(stolos_url)], project_uuid) _initialize_project(stolos_url, project) _initialize_services() click.echo("\t\tOkay.") click.echo('Your project is ready! Run "stolos up" to launch it!')
def info(**kwargs): _ensure_stolos_directory() cnf = config.get_config() stolos_url = cnf["user"]["default-api-server"] _ensure_logged_in(stolos_url) headers = ["UUID", "Stack", "Public URL"] project = api.projects_retrieve(cnf["user"][_get_hostname(stolos_url)], cnf["project"]["uuid"]) uuid = project["uuid"] stack = "-" if project["stack"]: stack = project["stack"]["slug"] domain = project["routing_config"]["domain"] projects = [(uuid, stack, domain)] click.echo(tabulate(projects, headers=headers))
def projects_list(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) cnf = config.get_config() stolos_url = kwargs.get("stolos_url") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] headers = ["UUID", "Stack", "Public URL"] projects = [] for project in api.projects_list(cnf["user"][_get_hostname(stolos_url)]): uuid = project["uuid"] stack = "-" if project["stack"]: stack = project["stack"]["slug"] domain = project["routing_config"]["domain"] projects.append((uuid, stack, domain)) click.echo(tabulate(projects, headers=headers))
def up(detach, logs, build): _ensure_stolos_directory() _ensure_logged_in() cnf = config.get_config() _config_environ(cnf) click.echo("Syncing...") if _sync(False).wait() != 0: click.echo("There was an error with the sync") return click.echo("Okay.") click.echo("Starting services...") compose_args = ["up", "-d", "--remove-orphans"] if build: compose_args.append("--build") if _compose(compose_args).wait() != 0: click.echo("There was an error with starting your services") return click.echo("Started services at {}".format(cnf["project"]["public-url"])) if detach: return handler = InteruptHandler() signal.signal(signal.SIGINT, handler) processes = [("Syncing", _sync(True))] if logs: compose_args = ["logs", "--tail=20", "-f"] if _is_windows(): compose_args.append("--no-color") processes.append(("Services", _compose(compose_args))) exit = "" while not exit: for process_name, process in processes: if process.poll(): if handler.state == 0: exit = '{} exited with exit code "{}"'.format( process_name, process.returncode) for _, p in processes: if p is not process: p.terminate() else: exit = "Terminated by user" break time.sleep(1) for _, p in processes: p.wait() click.echo(exit)
def create(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) cnf = config.get_config() stolos_url = kwargs.pop("stolos_url") project_directory = kwargs.pop("project_directory") if not stolos_url: stolos_url = cnf["user"]["default-api-server"] if not kwargs["public_url"]: if kwargs["stack"]: company, stack_name = kwargs["stack"].split("/") fmt_str = "{company}-{stack_name}-{username}-{hex}.{server}" kwargs["public_url"] = fmt_str.format( company=company, stack_name=stack_name, username=cnf["user"][_get_hostname(stolos_url)]["username"], hex="".join( [random.choice(string.ascii_lowercase) for _ in range(6)]), server=stolos_url, ) else: fmt_str = "{username}-{hex}.{server}" kwargs["public_url"] = fmt_str.format( username=cnf["user"][_get_hostname(stolos_url)]["username"], hex="".join( [random.choice(string.ascii_lowercase) for _ in range(6)]), server=stolos_url, ) click.echo('Assigning random public URL "{}"'.format( kwargs["public_url"])) click.echo('Creating project "{}"...'.format(project_directory), nl=False) project = api.projects_create(cnf["user"][_get_hostname(stolos_url)], **kwargs) if not os.path.exists(project_directory): os.makedirs(project_directory) os.chdir(project_directory) _initialize_project(stolos_url, project) click.echo("\t\tOk.") _initialize_services() if project_directory == ".": click.echo('Your project is ready! Run "stolos up" to launch it!') return click.echo( ('Your project is ready! Change directory with "cd {0}" and run ' '"stolos up" to launch it!').format(project_directory))
def delete(**kwargs): _ensure_logged_in(kwargs["stolos_url"]) project_uuid = kwargs.pop("project_uuid") if (not project_uuid and not _ensure_stolos_directory(base_directory=None, raise_exc=False)): raise exceptions.CLIRequiredException("project-uuid") cnf = config.get_config() stolos_url = kwargs.pop("stolos_url") remove_directory = False if not project_uuid: project_uuid = cnf["project"]["uuid"] remove_directory = True if not stolos_url: stolos_url = cnf["user"]["default-api-server"] click.echo('Deleting project "{}"...'.format(project_uuid), nl=False) api.projects_remove(cnf["user"][_get_hostname(stolos_url)], project_uuid) click.echo("\t\tOkay.") if remove_directory: click.echo("Clearing up Docker resources...") # Also, remove any leftover project resources. _config_environ(cnf) _compose(["down"]).wait() _deinitialize_project() click.echo("Okay.")
def compose(ctx): _ensure_stolos_directory() cnf = config.get_config() _config_environ(cnf) _compose(ctx.args).wait()
def launch(**kwargs): _ensure_stolos_directory() cnf = config.get_config() public_url = _get_url_for_service_port(cnf, **kwargs) click.echo("Opening http://{}...".format(public_url)) click.launch("http://{}".format(public_url))