Esempio n. 1
0
def cli_node_new_configuration(name, environment, system_folders):
    """Create a new configation file.

    Checks if the configuration already exists. If this is not the case
    a questionaire is invoked to create a new configuration file.
    """
    # select configuration name if none supplied
    if not name:
        name = q.text("Please enter a configuration-name:").ask()
        name_new = name.replace(" ", "-")
        if name != name_new:
            info(f"Replaced spaces from configuration name: {name}")
            name = name_new

    if not environment:
        environment = q.select(
            "Please select the environment you want to configure:",
            ["application", "prod", "acc", "test", "dev"]).ask()

    # Check that we can write in this folder
    if not check_config_write_permissions(system_folders):
        error("Your user does not have write access to all folders. Exiting")
        exit(1)

    # check that this config does not exist
    if NodeContext.config_exists(name, environment, system_folders):
        raise FileExistsError(f"Configuration {name} and environment"
                              f"{environment} already exists!")

    # create config in ctx location
    cfg_file = configuration_wizard("node", name, environment, system_folders)
    info(f"New configuration created: {Fore.GREEN}{cfg_file}{Style.RESET_ALL}")
Esempio n. 2
0
def cli_node_start(name, config, environment, system_folders, dockerized):
    """Start the node instance.

    If no name or config is specified the default.yaml configuation is used.
    In case the configuration file not excists, a questionaire is
    invoked to create one. Note that in this case it is not possible to
    specify specific environments for the configuration (e.g. test,
    prod, acc).
    """
    ContextClass = DockerNodeContext if dockerized else NodeContext

    # in case a configuration file is given, we bypass all the helper
    # stuff since you know what you are doing
    if config:
        name = Path(config).stem
        ctx = ContextClass(name, environment, system_folders, config)

    else:
        # in case no name is supplied, ask user to select one
        if not name:
            name, environment = select_configuration_questionaire(
                "node", system_folders)

        # check that config exists in the APP, if not a questionaire will
        # be invoked
        if not ContextClass.config_exists(name, environment, system_folders):
            question = f"Configuration '{name}' using environment"
            question += f" '{environment}' does not exist.\n  Do you want to"
            question += f" create this config now?"

            if q.confirm(question).ask():
                configuration_wizard("node", name, environment, system_folders)

            else:
                sys.exit(0)

        # create dummy node context
        ctx = ContextClass(name, environment, system_folders)

    # run the node application
    node.run(ctx)
Esempio n. 3
0
def cli_server_new(name, environment, system_folders):
    """Create new configuration."""

    if not name:
        name = q.text("Please enter a configuration-name:").ask()
        name_new = name.replace(" ", "-")
        if name != name_new:
            info(f"Replaced spaces from configuration name: {name}")
            name = name_new

    # check that this config does not exist
    try:
        if ServerContext.config_exists(name, environment, system_folders):
            error(f"Configuration {Fore.RED}{name}{Style.RESET_ALL} with "
                  f"environment {Fore.RED}{environment}{Style.RESET_ALL} "
                  f"already exists!")
            exit(1)
    except Exception as e:
        print(e)
        exit(1)

    # Check that we can write in this folder
    if not check_config_write_permissions(system_folders):
        error("Your user does not have write access to all folders. Exiting")
        info(f"Create a new server using '{Fore.GREEN}vserver new "
             "--user{Style.RESET_ALL}' instead!")
        exit(1)

    # create config in ctx location
    cfg_file = configuration_wizard("server",
                                    name,
                                    environment=environment,
                                    system_folders=system_folders)
    info(f"New configuration created: {Fore.GREEN}{cfg_file}{Style.RESET_ALL}")

    # info(f"root user created.")
    flag = "" if system_folders else "--user"
    info(f"You can start the server by running "
         f"{Fore.GREEN}vserver start {flag}{Style.RESET_ALL}")
Esempio n. 4
0
def cli_node_start(name, config, environment, system_folders, image, keep,
                   mount_src):
    """Start the node instance.

        If no name or config is specified the default.yaml configuation is
        used. In case the configuration file not excists, a questionaire is
        invoked to create one. Note that in this case it is not possible to
        specify specific environments for the configuration (e.g. test,
        prod, acc).
    """
    info("Starting node...")
    info("Finding Docker deamon")
    docker_client = docker.from_env()
    check_if_docker_deamon_is_running(docker_client)

    if config:
        name = Path(config).stem
        ctx = NodeContext(name, environment, system_folders, config)

    else:
        # in case no name is supplied, ask the user to select one
        if not name:
            name, environment = select_configuration_questionaire(
                "node", system_folders)

        # check that config exists, if not a questionaire will be invoked
        if not NodeContext.config_exists(name, environment, system_folders):
            question = f"Configuration '{name}' using environment"
            question += f" '{environment}' does not exist.\n  Do you want to"
            question += f" create this config now?"

            if q.confirm(question).ask():
                configuration_wizard("node", name, environment, system_folders)

            else:
                error("Config file couldn't be loaded")
                sys.exit(0)

        NodeContext.LOGGING_ENABLED = False
        ctx = NodeContext(name, environment, system_folders)

    # check that this node is not already running
    running_nodes = docker_client.containers.list(
        filters={"label": f"{APPNAME}-type=node"})

    suffix = "system" if system_folders else "user"
    for node in running_nodes:
        if node.name == f"{APPNAME}-{name}-{suffix}":
            error(f"Node {Fore.RED}{name}{Style.RESET_ALL} is already running")
            exit(1)

    # make sure the (host)-task and -log dir exists
    info("Checking that data and log dirs exist")
    ctx.data_dir.mkdir(parents=True, exist_ok=True)
    ctx.log_dir.mkdir(parents=True, exist_ok=True)

    if image is None:
        image = ctx.config.get(
            "image", "harbor.vantage6.ai/infrastructure/node:latest")

    info(f"Pulling latest node image '{image}'")
    try:
        # docker_client.images.pull(image)
        pull_if_newer(image)

    except Exception:
        warning(' ... alas, no dice!')
    else:
        info(" ... success!")

    info("Creating Docker data volume")
    data_volume = docker_client.volumes.create(
        f"{ctx.docker_container_name}-vol")

    info("Creating file & folder mounts")
    # FIXME: should only mount /mnt/database.csv if it is a file!
    # FIXME: should obtain mount points from DockerNodeContext
    mounts = [
        # (target, source)
        ("/mnt/database.csv", str(ctx.databases["default"])),
        ("/mnt/log", str(ctx.log_dir)),
        ("/mnt/data", data_volume.name),
        ("/mnt/config", str(ctx.config_dir)),
        ("/var/run/docker.sock", "/var/run/docker.sock"),
    ]

    if mount_src:
        # If mount_src is a relative path, docker willl consider it a volume.
        mount_src = os.path.abspath(mount_src)
        mounts.append(('/vantage6/vantage6-node', mount_src))

    # FIXME: Code duplication: Node.__init__() (vantage6/node/__init__.py)
    #   uses a lot of the same logic. Suggest moving this to
    #   ctx.get_private_key()
    filename = ctx.config.get("encryption", {}).get("private_key")

    # filename may be set to an empty string
    if not filename:
        filename = 'private_key.pem'

    # Location may be overridden by the environment
    filename = os.environ.get('PRIVATE_KEY', filename)

    # If ctx.get_data_file() receives an absolute path, it is returned as-is
    fullpath = Path(ctx.get_data_file(filename))

    if fullpath:
        if Path(fullpath).exists():
            mounts.append(("/mnt/private_key.pem", str(fullpath)))
        else:
            warning(f"private key file provided {fullpath}, "
                    "but does not exists")

    volumes = {}
    for mount in mounts:
        volumes[mount[1]] = {'bind': mount[0], 'mode': 'rw'}

    # Be careful not to use 'environment' as it would override the function
    # argument ;-).
    env = {
        "DATA_VOLUME_NAME": data_volume.name,
        "DATABASE_URI": "/mnt/database.csv",
        "PRIVATE_KEY": "/mnt/private_key.pem"
    }

    cmd = f'vnode-local start -c /mnt/config/{name}.yaml -n {name} -e '\
          f'{environment} --dockerized'

    info(f"Runing Docker container")
    # debug(f"  with command: '{cmd}'")
    # debug(f"  with mounts: {volumes}")
    # debug(f"  with environment: {env}")

    container = docker_client.containers.run(image,
                                             command=cmd,
                                             volumes=volumes,
                                             detach=True,
                                             labels={
                                                 f"{APPNAME}-type": "node",
                                                 "system": str(system_folders),
                                                 "name": ctx.config_file_name
                                             },
                                             environment=env,
                                             name=ctx.docker_container_name,
                                             auto_remove=not keep,
                                             tty=True)

    info(f"Success! container id = {container}")