Beispiel #1
0
def run_view(obj, namespace, function, args, post, upload, save_as):
    """Run a view in given plugin NAMESPACE and FUNCTION with ARGS.

    e.g.

    \b
    encapsia run view example_namespace test_view 3 tim limit=45

    If an ARGS contains an "=" sign then send it as an optional query string argument.
    Otherwise send it as a URL path segment.

    """
    api = lib.get_api(**obj)
    query_args = {}
    path_segments = []
    for arg in args:
        if "=" in arg:
            left, right = arg.split("=", 1)
            query_args[left] = right
        else:
            path_segments.append(arg)

    result = api.run_view(
        namespace,
        function,
        view_arguments=path_segments,
        view_options=query_args,
        use_post=post,
        upload=upload,
        download=save_as,
    )
    _log_result(result)
Beispiel #2
0
 def make_from_encapsia(host: str) -> PluginInfos:
     # TODO: use pluginsmanager.plugins() if it exists
     api = lib.get_api(host=host)
     raw_info = lib.resilient_call(
         api.run_view,
         "pluginsmanager",
         "installed_plugins_with_tags",
         description=
         "api.run_view('pluginsmanager', 'installed_plugins_with_tags')",
         idempotent=True,
     )
     pis = []
     for i in raw_info:
         tags = i.get("plugin_tags")
         if not isinstance(tags, list):
             tags = []
         try:
             variant = get_variant_from_tags(tags)
         except TooManyVariantTagsError as e:
             lib.log_error(f"Error in {i['name']} tag list: {e}")
         pi = PluginInfo.make_from_name_variant_version(
             i["name"], variant, i["version"])
         pi.extras.update({
             "description": i["description"],
             "installed": _format_datetime(i["when"]),
             "plugin-tags": ", ".join(sorted(tags)),
         })
         pis.append(pi)
     return PluginInfos(pis)
Beispiel #3
0
def run_job(obj, namespace, function, args, upload, save_as):
    """Run a job in given plugin NAMESPACE and FUNCTION with ARGS.

    E.g.

    \b
    encapsia run job example_namespace test_module.test_function x=3 y=tim "z=hello"

    Note that all args must be named and the values are all considered strings (not
    least because arguments are encoded over a URL string).

    """
    api = lib.get_api(**obj)
    params = {}
    for arg in args:
        left, right = arg.split("=", 1)
        params[left.strip()] = right.strip()
    result = lib.run_job(
        api,
        namespace,
        function,
        params,
        f"Running job {namespace}.{function}",
        upload=upload,
        download=save_as,
    )
    _log_result(result)
Beispiel #4
0
def dev_update(obj, directory):
    """Update plugin parts which have changed since previous update.

    Optionally pass in the DIRECTORY of the plugin (defaults to cwd).

    """
    directory = Path(directory)
    plugin_toml_path = directory / "plugin.toml"
    if not plugin_toml_path.exists():
        lib.log_error("Not in a plugin directory.", abort=True)

    with _get_modified_plugin_directories(
            directory,
            reset=obj["plugins_force"]) as modified_plugin_directories:
        if modified_plugin_directories:
            with lib.temp_directory() as temp_directory:
                shutil.copy(plugin_toml_path, temp_directory)
                for modified_directory in modified_plugin_directories:
                    lib.log(f"Including: {modified_directory}")
                    shutil.copytree(
                        directory / modified_directory,
                        temp_directory / modified_directory,
                    )
                api = lib.get_api(**obj)
                result = lib.run_plugins_task(
                    api,
                    "dev_update_plugin",
                    dict(),
                    "Uploading to server",
                    data=lib.create_targz_as_bytes(temp_directory),
                )
                if not result:
                    raise _PluginsTaskError
        else:
            lib.log("Nothing to do.")
Beispiel #5
0
def logs(obj, plugins):
    """Print the latest install logs for given plugins."""
    api = lib.get_api(**obj)
    # Despite the name, this only fetches the latest log for each plugin, not all!
    raw_info = lib.resilient_call(
        api.run_view,
        "pluginsmanager",
        "all_plugin_logs",
        description="api.run_view('pluginsmanager', 'all_plugin_logs')",
        idempotent=True,
    )
    if plugins:
        # Filter to specified plugins.
        raw_info = [i for i in raw_info if i["name"] in plugins]
    for i in raw_info:
        for f in (
                "name",
                "version",
                "description",
                "action",
                "server",
                "success",
                "when",
        ):
            lib.log(f"{f.capitalize()}: {i[f]}")
        lib.log("Logs:")
        if i["success"]:
            lib.log_output(i["output"].strip())
        else:
            lib.log_error(i["output"].strip())
        lib.log("")
    if len(raw_info) == 0 and len(plugins) > 0:
        lib.log(
            "No logs found. Note that any plugins must be exact name matches without version info."
        )
Beispiel #6
0
def set(obj, key, value):
    """Store value against given key."""
    api = lib.get_api(**obj)
    value = lib.parse(value, "json")
    lib.resilient_call(
        api.set_config, key, value, description=f"api.set_config({key}, <value>)"
    )
Beispiel #7
0
def get(obj, key):
    """Retrieve value against given key."""
    api = lib.get_api(**obj)
    value = lib.resilient_call(
        api.get_config, key, description=f"api.get_config({key})", idempotent=True
    )
    lib.pretty_print(value, "json")
Beispiel #8
0
def save(obj, output):
    """Save entire configuration to given file."""
    api = lib.get_api(**obj)
    config = lib.resilient_call(
        api.get_all_config, description="api.get_all_config()", idempotent=True
    )
    lib.pretty_print(config, "json", output=output)
Beispiel #9
0
def show(obj):
    """Show entire configuration."""
    api = lib.get_api(**obj)
    config = lib.resilient_call(
        api.get_all_config, description="api.get_all_config()", idempotent=True
    )
    lib.pretty_print(config, "json")
Beispiel #10
0
def create_fixture(obj, name):
    """Create new fixture with given name."""
    api = lib.get_api(**obj)
    lib.log_output(
        lib.dbctl_action(
            api, "create_fixture", dict(name=name), f"Creating fixture {name}"
        )
    )
Beispiel #11
0
 def make_from_encapsia(host):
     api = lib.get_api(host=host)
     raw_info = api.run_view("pluginsmanager",
                             "installed_plugins_with_tags")
     return PluginInfos([
         PluginInfo.make_from_name_version(i["name"], i["version"])
         for i in raw_info
     ])
def remove_task(obj, scheduled_task_id):
    """Remove scheduled task by id."""
    api = lib.get_api(**obj)
    lib.run_plugins_task(
        api,
        "remove_scheduled_task",
        dict(scheduled_task_id=scheduled_task_id),
        "Removing scheduled tasks",
    )
def remove_tasks_in_namespace(obj, namespace):
    """Remove all scheduled tasks in given namespace."""
    api = lib.get_api(**obj)
    lib.run_plugins_task(
        api,
        "remove_scheduled_tasks_in_namespace",
        dict(namespace=namespace),
        "Removing scheduled tasks",
    )
Beispiel #14
0
def dev_create(obj, namespace, n_task_workers):
    """Create namespace of given name. Only useful during developmment."""
    api = lib.get_api(**obj)
    lib.run_plugins_task(
        api,
        "dev_create_namespace",
        dict(namespace=namespace, n_task_workers=n_task_workers),
        "Creating namespace",
    )
Beispiel #15
0
def delete_fixture(obj, name, yes):
    """Delete fixture with given name."""
    if not yes:
        click.confirm(f'Are you sure you want to delete fixture "{name}"?', abort=True)
    api = lib.get_api(**obj)
    lib.log_output(
        lib.dbctl_action(
            api, "delete_fixture", dict(name=name), f"Deleting fixture {name}"
        )
    )
Beispiel #16
0
def use_fixture(obj, name, yes):
    """Switch to fixture with given name."""
    if not yes:
        click.confirm(
            f'Are you sure you want to change the database to fixture "{name}"?',
            abort=True,
        )
    api = lib.get_api(**obj)
    poll, NoTaskResultYet = api.dbctl_action("use_fixture", dict(name=name))
    lib.log(f"Requested change to fixture {name}.")
    lib.log("Please verify by other means (e.g. look at the logs).")
Beispiel #17
0
def uninstall(obj, namespace):
    """Uninstall named plugin."""
    if not obj["force"]:
        click.confirm(
            f"Are you sure you want to uninstall the plugin " +
            f"(delete all!) from namespace {namespace}",
            abort=True,
        )
    api = lib.get_api(**obj)
    lib.run_plugins_task(api, "uninstall_plugin", dict(namespace=namespace),
                         f"Uninstalling {namespace}")
Beispiel #18
0
def status(obj, plugins):
    """Print information about (successfully) installed plugins."""
    local_versions = PluginInfos.make_from_local_store(
        obj["plugins_local_dir"]).filter_to_latest()
    api = lib.get_api(**obj)
    raw_info = api.run_view("pluginsmanager", "installed_plugins_with_tags")
    plugin_infos = []
    for i in raw_info:
        pi = PluginInfo.make_from_name_version(i["name"], i["version"])

        available_pi = local_versions.latest_version_matching_spec(i["name"])
        if available_pi:
            temp = available_pi.formatted_version()
            available = "<same>" if temp == pi.formatted_version() else temp
        else:
            available = ""

        pi.extras = {
            "description":
            i["description"],
            "plugin-tags": (", ".join(sorted(i["plugin_tags"])) if isinstance(
                i["plugin_tags"], list) else ""),
            "installed":
            _format_datetime(i["when"]),
            "available":
            available,
        }

        plugin_infos.append(pi)

    if plugins:
        plugin_infos = PluginInfos(plugin_infos).filter_to_specs(plugins).pis

    for i in raw_info:
        i["version"] = PluginInfo.make_from_name_version(
            i["name"], i["version"]).formatted_version()
    headers = [
        "name",
        "version",
        "available",
        "description",
        "installed",
        "plugin-tags",
    ]
    info = ([
        pi.name,
        pi.formatted_version(),
        pi.extras["available"],
        pi.extras["description"],
        pi.extras["installed"],
        pi.extras["plugin-tags"],
    ] for pi in plugin_infos)
    lib.log(tabulate(info, headers=headers))
    _log_message_explaining_semver()
Beispiel #19
0
def extend(obj, lifespan):
    """Extend the lifespan of token and update encapsia credentials (if used)."""
    api = lib.get_api(**obj)
    new_token = api.login_again(lifespan=lifespan)
    host = get_host(obj)
    if host:
        store = CredentialsStore()
        url, old_token = store.get(host)
        store.set(host, url, new_token)
        lib.log("Encapsia credentials file updated.")
    else:
        lib.log(new_token)
Beispiel #20
0
def info(obj, logs):
    """Provide information about installed plugins."""
    api = lib.get_api(**obj)
    raw_info = api.run_view("pluginsmanager", "installed_plugins_with_tags")
    headers = ["name", "version", "description", "when", "plugin_tags"]
    info = ([p[h] for h in headers] for p in raw_info)
    lib.log(tabulate(info, headers=headers))
    if logs:
        for i in raw_info:
            name, output = i["name"], i["output"]
            lib.log(f"\n[Install log for {name}]")
            lib.log_output(output.strip())
Beispiel #21
0
def export_users_and_roles(obj, filename, with_roles):
    """Export users (and roles) to given TOML file."""
    filename = Path(filename)
    api = lib.get_api(**obj)
    export_data = {}
    export_data["users"] = api.get_all_users()
    if with_roles:
        export_data["roles"] = api.get_all_roles()
    with filename.open(mode="w") as f:
        lib.pretty_print(export_data, "toml", f)
        lib.log_output(
            f"Exported {len(export_data['users'])} users and {len(export_data.get('roles', []))} roles to {filename}"
        )
Beispiel #22
0
def import_users_and_roles(obj, filename):
    """Import users (and roles) from given TOML file."""
    filename = Path(filename)
    api = lib.get_api(**obj)
    import_data = lib.read_toml(filename)
    users = import_data.get("users", [])
    roles = import_data.get("roles", [])
    if users:
        api.post("users", json=users)
    if roles:
        api.post("roles", json=roles)
    lib.log_output(
        f"Imported {len(users)} users and {len(roles)} roles from {filename}")
Beispiel #23
0
def expire(obj):
    """Expire token from server, and update encapsia credentials if used."""
    api = lib.get_api(**obj)
    try:
        api.delete("logout")
        lib.log("Expired token on server.")
    except EncapsiaApiError as e:
        lib.log_error("Failed to expire given token!")
        lib.log_error(str(e), abort=True)
    host = get_host(obj)
    if host:
        CredentialsStore().remove(host)
        lib.log("Removed entry from encapsia credentials file.")
def backup(obj, filename):
    """Backup database to given filename. (or create a temp one if not given)."""
    if filename:
        filename = Path(filename)
    api = lib.get_api(**obj)
    handle = lib.dbctl_action(api, "backup_database", dict(), "Backing up database")
    temp_filename = Path(
        api.dbctl_download_data(handle)
    )  # TODO remove Path() once encapsia_api switched over to pathlib
    if filename is None:
        filename = temp_filename
    else:
        temp_filename.rename(filename)
    lib.log(f"Downloaded {filename.stat().st_size} bytes to {filename}")
Beispiel #25
0
def shell(obj):
    """Launch an httpie interactive shell with passed-in credentials."""
    try:
        importlib.import_module("http_prompt")
    except ModuleNotFoundError:
        lib.log_error("Please install http-prompt first.", abort=True)
    api = lib.get_api(**obj)
    argv = [
        "http-prompt",
        api.url,
        f"Authorization: Bearer {api.token}",
        "Accept: application/json",
    ]
    subprocess.run(argv)
Beispiel #26
0
def uninstall(obj, show_logs, namespaces):
    """Uninstall named plugin(s)."""
    if namespaces and not obj["plugins_force"]:
        lib.log("Preparing to uninstall: " + ", ".join(namespaces))
        click.confirm(
            "Are you sure?",
            abort=True,
        )
    api = lib.get_api(**obj)
    for namespace in namespaces:
        lib.run_plugins_task(
            api,
            "uninstall_plugin",
            dict(namespace=namespace),
            f"Uninstalling {namespace}",
            print_output=show_logs,
        )
def restore(obj, filename, yes):
    """Restore database from given backup file."""
    filename = Path(filename)
    if not yes:
        click.confirm(
            f'Are you sure you want to restore the database from "{filename}"?',
            abort=True,
        )
    api = lib.get_api(**obj)
    handle = api.dbctl_upload_data(filename)
    # On a restore, the server is temporarily stopped.
    # This means that attempts to use it will generate a 500 error when
    # Nginx tries to check the permission.
    # Further, the current token may no longer work.
    poll, NoResultYet = api.dbctl_action("restore_database", dict(data_handle=handle))
    lib.log("Database restore requested.")
    lib.log("Please verify by other means (e.g. look at the logs).")
Beispiel #28
0
def list_users(obj, super_users, system_users, all_users):
    """List out information about users."""
    api = lib.get_api(**obj)
    if not (super_users or system_users or all_users):
        # If no specific type of user specified then assume all-users was intended.
        all_users = True
    if super_users:
        lib.log_output("[Super users]")
        users = api.get_super_users()
        headers = ["email", "first_name", "last_name"]
        lib.log_output(
            tabulate.tabulate(
                [[getattr(row, header) for header in headers]
                 for row in users],
                headers=headers,
            ))
        lib.log_output()
    if system_users:
        lib.log_output("[System users]")
        users = api.get_system_users()
        headers = ["email", "description", "capabilities"]
        lib.log_output(
            tabulate.tabulate(
                [[getattr(row, header) for header in headers]
                 for row in users],
                headers=headers,
            ))
        lib.log_output()
    if all_users:
        lib.log_output("[All users]")
        users = api.get_all_users()
        headers = [
            "email",
            "first_name",
            "last_name",
            "role",
            "enabled",
            "is_site_user",
        ]
        lib.log_output(
            tabulate.tabulate([[row[header] for header in headers]
                               for row in users],
                              headers=headers))
        lib.log_output()
def add_task(obj, description, task_host, task_token, namespace, task, params,
             cron, jitter):
    """Add new scheduled task."""
    api = lib.get_api(**obj)
    lib.run_plugins_task(
        api,
        "add_scheduled_task",
        dict(
            description=description,
            host=task_host,
            token=task_token,
            namespace=namespace,
            task=task,
            params=params,
            cron=cron,
            jitter=jitter,
        ),
        "Adding scheduled task",
    )
Beispiel #30
0
def install(obj, versions, plugin_filenames):
    """Install plugins from files and/or an optional versions.toml file.

    Plugins named directly will be put in the cache before being installed.

    Plugins specified in the versions.toml file will be taken from the cache.

    """
    plugins_cache_dir = obj["plugins_cache_dir"]
    api = lib.get_api(**obj)
    for plugin_filename in plugin_filenames:
        plugin_filename = Path(plugin_filename).resolve()
        _fetch_plugin_and_store_in_cache(plugin_filename.as_uri(),
                                         plugins_cache_dir,
                                         force=True)
        _install_plugin(api, plugins_cache_dir / plugin_filename.name)
    if versions:
        versions = Path(versions)
        for name, version in lib.read_toml(versions).items():
            _install_plugin(
                api, plugins_cache_dir / f"plugin-{name}-{version}.tar.gz")