Exemplo n.º 1
0
def start(docker_compose_path: str, docker_compose_override_path: str,
          stack_name: str, enable_mon: bool):
    log.info("Deploying Coral...")
    swarmadmin.deploy_stack(docker_compose_path, docker_compose_override_path,
                            stack_name)

    if (enable_mon):
        log_stdout.info("")
        log.info("Deploying Monitor stack...")
        swarmadmin.deploy_stack("./monitor/docker-compose.monitor.yml", None,
                                f"{stack_name}_monitor")
Exemplo n.º 2
0
def remove_coral_objects(obj_type: str,
                         silent: bool = False,
                         list_found_objs: bool = True,
                         prompt: bool = True,
                         is_dep: bool = False):
    if not silent:
        log.info(
            f"Removing{' dependent ' if is_dep else ' '}Coral {obj_type}s...")

    cleanup_params = settings.get("cleanup_params")[obj_type]
    rm_confirmation_msg, all_, quiet, format_, filters, force = [
        value for value in cleanup_params.values()
    ]

    obj_list = get_coral_objs(obj_type,
                              all_=all_,
                              quiet=quiet,
                              format_=f"\"{format_}\"",
                              filters=filters)

    if obj_list:
        bullets_obj_list = "\n".join(
            [f"- {line}" for line in obj_list.split("\n")])

        if list_found_objs and not silent:
            log.info(
                f"The following Coral {obj_type}s were found:\n{bullets_obj_list}"
            )
            log_stdout.info("")

        if prompt:
            question = f"{rm_confirmation_msg}\nAre you sure you want to remove these {obj_type}s? (y/n) "
            answer = fish.valid_answer_from_prompt(
                question, valid_answers=settings.get("prompt_boolean_answers"))
            log_file.info(question.replace("\n", " ") + answer)

        if not prompt or fish.user_prompt_yes(answer):
            remove_coral_obj_dependencies(obj_type, silent=silent)

            obj_ids = [
                i for i, j in (obj.split("\t") for obj in obj_list.split("\n"))
            ]
            if not silent: log.info(f"Removing {obj_type}s...")
            res = swarmadmin.rm(obj_type,
                                obj_ids,
                                force=force,
                                suppress_stdout=silent)
            fish.handle(res)
            if not silent: log.info(f"Done!")
    else:
        if not silent:
            log.info(f"No Coral {obj_type}s were found. Skipping...")
Exemplo n.º 3
0
def update():
    question = "Coral will be stopped in order to perform the update. Do you wish to continue? (y/n) "
    answer = fish.valid_answer_from_prompt(
        question, valid_answers=settings.get("prompt_boolean_answers"))
    log_file.info(question + answer)

    if fish.user_prompt_yes(answer):
        docker_compose_path = settings.get("compose_path")
        original_compose_file = None

        log.info("Updating Coral Docker images...")

        # check if a current deployment is using dev images
        curr_depl_conf = deploy.get_curr_depl_config()
        if curr_depl_conf is not None:
            original_compose_file = fish.read_file(docker_compose_path)
            dev = curr_depl_conf["advanced"]["dev_images"]
            if dev:
                deploy.switch_to_dev_images(
                    docker_compose_path)  # switch to dev images

        try:
            registry_login()
            pull_coral_images(docker_compose_path)
        finally:
            if original_compose_file is not None:
                fish.write_file(original_compose_file, docker_compose_path)

        log_stdout.info("")
        log.info("Coral Docker images successfully updated!")

        curr_depl_conf = deploy.get_curr_depl_config()

        if (curr_depl_conf is None):
            log.info("Cannot start Coral. Please deploy first.")
        else:
            domain = curr_depl_conf["basic"][
                "domain"]  # needed for logs after deployment
            deploy.prepare(skip_depl_config=True,
                           is_update=True,
                           docker_compose_path=docker_compose_path,
                           domain=domain,
                           email=None,
                           cert_type=None,
                           stack_name=None,
                           no_prompt=True)
Exemplo n.º 4
0
def prompt_secrets(secrets: list):
    log_stdout.info("Please provide the secrets below:")
    for secret_name in secrets:
        secret_content = getpass.getpass(prompt=f"  {secret_name}: ")
        min_secret_len = settings.get("min_secret_len")

        while len(secret_content) < min_secret_len:
            log.info(
                f"Secrets must use at least {min_secret_len} characters. Try again:"
            )
            secret_content = getpass.getpass(prompt=f"  {secret_name}: ")

        swarmadmin.create_secret(secret_content, secret_name, "system=Coral")

    log_stdout.warn(
        "\n\033[1;31mIf you forget your passwords, you will loose access to your data.\nIt is highly recommended that you also manually store the passwords somewhere else safe.\033[0m"
    )
    input("Press Enter to continue...")
Exemplo n.º 5
0
def scale_all_services(n_replicas: int, quiet: bool = False):
    coral_services = get_coral_objs("service", quiet=True, format_="{{.Name}}")

    if coral_services:
        coral_services_list = coral_services.split('\n')
        n_services = len(coral_services_list)

        # scale services one by one so progress can be displayed in stdout
        for i, service in enumerate(coral_services_list):
            # skip if service is already stopped/started
            res = swarmadmin.get_service_replicas(service)
            fish.handle(res)
            curr_replicas = int(res["stdout"])
            if n_replicas == 0 and curr_replicas == 0 or n_replicas == 1 and curr_replicas == 1:
                state = "stopped" if n_replicas == 0 else "running"
                if not quiet:
                    log.info(f"{service} is already {state}. Skipping...")
                    fish.print_progress_bar(i + 1,
                                            n_services,
                                            prefix=f"Progress:",
                                            suffix="Complete\n",
                                            length=50)
                continue

            # TODO: check if service image needs to be pulled and warn user
            res = swarmadmin.scale([service],
                                   n_replicas,
                                   suppress_stdout=quiet,
                                   suppress_stderr=quiet,
                                   timeout=settings.get("scaling_timeout"))
            if not quiet:
                fish.print_progress_bar(i + 1,
                                        n_services,
                                        prefix=f"Progress:",
                                        suffix="Complete\n",
                                        length=50)
            fish.handle(res)

        if not quiet: log_stdout.info("")

        return 0
    else:
        return 1
Exemplo n.º 6
0
def signal_handler(sig, frame):
    msg = "Cancelled by user. Exiting..."
    log_stdout.info("\n" + msg)
    log_file.info(msg)

    sys.exit(0)
Exemplo n.º 7
0
def parse_args(parser, skip_depl_config=False):
    parsed_args = parser.parse_args()
    help_msg = "Run with the '-h' flag for additional help."

    if len(sys.argv) == 1:  # print help when no args are provided
        parser.print_help()
    else:
        try:
            # if the version arg is present, print version and finish
            if parsed_args.version:
                log.info(settings.get("version"))
                return parsed_args

            used_depl_args = get_used_args_from_group(parsed_args,
                                                      "deployment")
            used_mgmt_args = get_used_args_from_group(parsed_args,
                                                      "management")

            # do not allow deployment args without using '--deploy'
            if len(used_depl_args) > 0 and "deploy" not in used_depl_args:
                first_offending_arg = get_first_used_arg_from_group(
                    parsed_args, "deployment")
                parser.error(
                    "Deployment arguments, such as '{0}', require '--deploy'.".
                    format(first_offending_arg))

            # do not allow management args if '--deploy' is used
            if parsed_args.deploy and len(used_mgmt_args) > 0:
                first_offending_arg = get_first_used_arg_from_group(
                    parsed_args, "management")
                parser.error(
                    "Management arguments, such as '{0}', are not allowed when using '--deploy'."
                    .format(first_offending_arg))

            str_central_conf = settings.get("custom_apache")["central_rule"]
            original_custom_file = fish.read_file(
                settings.get_custom_apache_conf_file_path())

            # check for presence of the central arg
            if parsed_args.deploy and parsed_args.central:
                if str_central_conf not in original_custom_file:
                    log.info(
                        "Including apache rule for central monitoring in " +
                        settings.get_custom_apache_conf_file_path())
                    original_custom_file = original_custom_file + str_central_conf
                    fish.write_file(
                        original_custom_file,
                        settings.get_custom_apache_conf_file_path())
            else:
                original_custom_file = original_custom_file.replace(
                    str_central_conf, "")
                fish.write_file(original_custom_file,
                                settings.get_custom_apache_conf_file_path())

            if parsed_args.deploy and not skip_depl_config:
                # check for presence of required deployment args and validate them
                required_depl_args = get_required_depl_args()

                missing = list(
                    filter(lambda x: x not in used_depl_args,
                           required_depl_args))

                if len(missing) > 0:
                    parser.error(
                        "Missing required deployment arguments: --{0}".format(
                            ", --".join(missing)))
                else:
                    if not fish.is_valid("domain", parsed_args.domain):
                        parser.error("Invalid domain: '{0}'".format(
                            parsed_args.domain))
                    if not fish.is_valid("email", parsed_args.email):
                        parser.error("Invalid email: '{0}'".format(
                            parsed_args.email))

                # make '--letsencrypt' and '--no-port-binding' incompatible
                if parsed_args.letsencrypt and parsed_args.no_port_binding:
                    parser.error(
                        "Incompatible arguments: '--letsencrypt' and '--no-port-binding'. Cannot issue a Let's Encrypt certificate if port binding to host is disabled."
                    )

                # do not allow dots in the stack name
                if parsed_args.stack_name is not None and "." in parsed_args.stack_name:
                    parser.error("The stack name cannot contain dots.")

                # validate advertise and proxy addresses, if present
                if parsed_args.addr and not fish.is_valid(
                        "ip", parsed_args.addr):
                    parser.error("Invalid IP address: '{0}'".format(
                        parsed_args.addr))
                if parsed_args.http_proxy and not fish.is_valid(
                        "address", parsed_args.http_proxy):
                    parser.error("Invalid proxy address: '{0}'".format(
                        parsed_args.http_proxy))
                if parsed_args.https_proxy and not fish.is_valid(
                        "address", parsed_args.https_proxy):
                    parser.error("Invalid proxy address: '{0}'".format(
                        parsed_args.https_proxy))
            else:
                # only allow one management argument at a time
                if len(used_mgmt_args) > 1:
                    parser.error(
                        "Only one management argument at a time is allowed.")
        except SystemExit as e:
            log_stdout.info("\n{0}".format(help_msg))
            sys.exit(e)

    return parsed_args
Exemplo n.º 8
0
        if parsed_args.start:
            status = manage.start()
        elif parsed_args.stop:
            status = manage.stop()
        elif parsed_args.restart:
            status = manage.restart()

    if status == 1:
        log.info(
            "No Coral services were found (Coral stack is not currently deployed)"
        )


if __name__ == "__main__":
    try:
        log_stdout.info(settings.get_intro())
        log_file.info("EXEC: [ {0} ]".format(", ".join(sys.argv)))

        signal.signal(signal.SIGINT, signal_handler)

        skip_depl_config = False
        if "--deploy" in sys.argv:
            skip_depl_config = deploy.check_skip_depl_config(
                "--test" in sys.argv)
        parser = config_arg_parser()
        parsed_args = parse_args(parser, skip_depl_config)
        main(parsed_args, skip_depl_config)
    except (Exception, SystemExit) as e:
        e_name = type(e).__name__
        if e_name == "SystemExit":
            if e == 1: log_file.exception(e_name)
Exemplo n.º 9
0
def prepare(skip_depl_config: bool,
            is_update: bool,
            docker_compose_path: str,
            domain: str,
            email: str,
            cert_type: str,
            stack_name: str,
            addr: str = None,
            http_proxy: str = None,
            https_proxy: str = None,
            no_port_binding: bool = False,
            dev: bool = False,
            no_prompt: bool = False,
            central: bool = False):
    env_file_path = settings.get_conf_file_path("stack")
    original_env_file = fish.read_file(env_file_path)
    original_compose_file = fish.read_file(docker_compose_path)
    deploy_monitor_stack = True

    try:
        if skip_depl_config:
            log.info("Skipping configuration of deployment parameters...")
            log_stdout.info("")
            curr_depl_conf = get_curr_depl_config()
            domain = curr_depl_conf["basic"]["domain"]
            stack_name = curr_depl_conf["basic"]["stack_name"]
            addr = curr_depl_conf["advanced"]["advertise_addr"]
            dev = curr_depl_conf["advanced"]["dev_images"]
            no_port_binding = curr_depl_conf["advanced"]["no_port_binding"]
            central = curr_depl_conf["advanced"]["central"]

        enable_swarm_mode(addr)
        log_stdout.info("")

        # remove any current Coral services
        if not is_update:
            log.info("Cleaning up any previously deployed Coral services...")
            manage.remove_coral_objects("service",
                                        list_found_objs=False,
                                        prompt=False)
            log_stdout.info("")

        # save deployment parameters
        if not skip_depl_config:
            config_depl_params(domain, email, cert_type, stack_name, addr,
                               http_proxy, https_proxy, no_port_binding, dev,
                               central)
            log_stdout.info("")

        # set environment variables using deployment perameters
        set_env_vars()
        log_stdout.info("")

        # check central monitoring
        deploy_monitor_stack = check_central_mon(env_file_path, no_prompt)
        log_stdout.info("")

        # make sure all required secrets exist (if not, prompt)
        check_secrets(docker_compose_path, no_prompt)
        log_stdout.info("")

        # temporarilly edit compose file to set dev images or unbind ports to host
        if dev: switch_to_dev_images(docker_compose_path)
        if no_port_binding: unbind_ports(docker_compose_path)

        # pull Coral images
        if not is_update:
            log.info("Pulling Coral Docker images...")
            manage.registry_login()
            manage.pull_coral_images(docker_compose_path)
            log_stdout.info("")

        # check_volumes()

        # deploy Coral
        docker_compose_override_path = get_compose_override_path(
            docker_compose_path)
        start(docker_compose_path, docker_compose_override_path, stack_name,
              deploy_monitor_stack)
        log_stdout.info("")

        # track progress
        #track_progress(stack_name, docker_compose_path)

        log.info("\033[1;32mCoral has been deployed!\033[0m")
        log_stdout.info(
            "After a few minutes, you should have access to the following services:\n"
            f"  \033[1;37mAgate\033[0m\t\t https://{domain}/auth\n"
            f"  \033[1;37mOpal\033[0m\t​\t https://{domain}/repo\n"
            f"  \033[1;37mMica\033[0m\t​\t https://{domain}/pub\n"
            f"  \033[1;37mMica Drupal\033[0m\t ​https://{domain}/cat​ (or just ​https://{domain}​)\n"
        )
    finally:
        # restore original env and docker-compose files
        fish.write_file(original_env_file, env_file_path)
        fish.write_file(original_compose_file, docker_compose_path)

    return 0