Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

This release marks most of the NC's dependencies in file `pyproject.toml` as _optional_. Please see updated installation instructions in the NC User Guide.

## Features

* #258: Added initial SCS CLI

## Refactorings

* #253: Made dependencies optional in file `pyproject.toml`
27 changes: 27 additions & 0 deletions exasol/nb_connector/cli/commands/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import sys
from pathlib import Path

import click

from exasol.nb_connector.cli.groups import cli
from exasol.nb_connector.cli.options import SCS_OPTIONS
from exasol.nb_connector.cli.param_wrappers import add_params


@cli.command()
@add_params(SCS_OPTIONS)
@click.option(
"--connect/--no-connect",
is_flag=True,
help="""Verify if connecting to the configured Exasol database instance
succeeds.""",
)
def check(scs_file: Path, connect: bool):
"""
Check the configuration currently saved to the Secure Configuration
Storage and verify if it contains all required parameters.

Optionally also verify if a connection to the configured Exasol database
instance is successful.
"""
pass
47 changes: 47 additions & 0 deletions exasol/nb_connector/cli/commands/configure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from pathlib import Path

from exasol.nb_connector.cli.groups import cli
from exasol.nb_connector.cli.options import (
DOCKER_DB_OPTIONS,
ONPREM_OPTIONS,
SAAS_OPTIONS,
)
from exasol.nb_connector.cli.param_wrappers import add_params


@cli.group()
def configure():
"""
Add configuration options to the Secure Configuration Storage.
"""
pass


@configure.command("onprem")
@add_params(ONPREM_OPTIONS)
def configure_onprem(scs_file: Path, **kwargs):
"""
Configure connection to an Exasol on-premise instance.
"""
pass


@configure.command("saas")
@add_params(SAAS_OPTIONS)
def configure_saas(scs_file: Path, **kwargs):
"""
Configure connection to an Exasol SaaS instance.

Configuring one of the parameters --saas-database-id and
--saas-database-name is sufficient.
"""
pass


@configure.command("docker-db")
@add_params(DOCKER_DB_OPTIONS)
def configure_docker_db(scs_file: Path, **kwargs):
"""
Configure connection to an Exasol Docker instance.
"""
pass
15 changes: 15 additions & 0 deletions exasol/nb_connector/cli/commands/show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from pathlib import Path

from exasol.nb_connector.cli.groups import cli
from exasol.nb_connector.cli.options import SCS_OPTIONS
from exasol.nb_connector.cli.param_wrappers import add_params


@cli.command()
@add_params(SCS_OPTIONS)
def show(scs_file: Path):
"""
Show the configuration currently saved to the Secure Configuration
Storage.
"""
pass
17 changes: 17 additions & 0 deletions exasol/nb_connector/cli/groups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import click


@click.group()
def cli():
"""
Manage application configuration data in the Secure Configuration
Storage (SCS).

You can set environment variables SCS_FILE and SCS_MASTER_PASSWORD
specifying the file containing the SCS and the master password for
accessing and encrypting the file, respectively.

Otherwise the scs commands will require the SCS file to be passed as
positional argument and the master password to be typed interactively.
"""
pass
12 changes: 12 additions & 0 deletions exasol/nb_connector/cli/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import exasol.nb_connector.cli.commands.check
import exasol.nb_connector.cli.commands.configure
import exasol.nb_connector.cli.commands.show
from exasol.nb_connector.cli.groups import cli


def main():
cli()


if __name__ == "__main__":
main()
21 changes: 21 additions & 0 deletions exasol/nb_connector/cli/options/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from exasol.nb_connector.cli.options.bucketfs import BUCKETFS_OPTIONS
from exasol.nb_connector.cli.options.common import (
COMMON_OPTIONS,
SAVE_OPTIONS,
SCS_OPTIONS,
)
from exasol.nb_connector.cli.options.docker_db import EXTRA_DOCKER_DB_OPTIONS
from exasol.nb_connector.cli.options.onprem import ONPREM_DB_OPTIONS
from exasol.nb_connector.cli.options.saas import EXTRA_SAAS_OPTIONS
from exasol.nb_connector.cli.options.ssl import SSL_OPTIONS


def _wrap(options):
return SCS_OPTIONS + SAVE_OPTIONS + options + COMMON_OPTIONS


DOCKER_DB_OPTIONS = _wrap(EXTRA_DOCKER_DB_OPTIONS)

SAAS_OPTIONS = _wrap(EXTRA_SAAS_OPTIONS + SSL_OPTIONS)

ONPREM_OPTIONS = _wrap(ONPREM_DB_OPTIONS + BUCKETFS_OPTIONS + SSL_OPTIONS)
77 changes: 77 additions & 0 deletions exasol/nb_connector/cli/options/bucketfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from exasol.nb_connector.ai_lab_config import AILabConfig as CKey
from exasol.nb_connector.cli.param_wrappers import (
ScsOption,
ScsSecretOption,
)

BUCKETFS_OPTIONS = [
ScsOption(
"--bucketfs-host",
metavar="HOST",
type=str,
default="localhost",
help="BucketFS host name",
scs_key=CKey.bfs_host_name,
),
ScsOption(
"--bucketfs-host-internal",
metavar="HOST",
type=str,
help="BucketFS internal host name [reads default from --bucketfs-host]",
scs_key=CKey.bfs_internal_host_name,
get_default_from="bucketfs_host",
),
ScsOption(
"--bucketfs-port",
metavar="PORT",
type=int,
default=2580,
help="BucketFS port",
scs_key=CKey.bfs_port,
),
ScsOption(
"--bucketfs-port-internal",
metavar="PORT",
type=int,
help="BucketFS internal port [reads default from --bucketfs-port]",
scs_key=CKey.bfs_internal_port,
get_default_from="bucketfs_port",
),
ScsOption(
"--bucketfs-user",
metavar="USERNAME",
type=str,
# should we add "w" as default here?
help="BucketFS user name",
scs_key=CKey.bfs_user,
),
ScsSecretOption(
"--bucketfs-password",
envvar="SCS_BUCKETFS_PASSWORD",
prompt="BucketFS write password",
scs_key=CKey.bfs_password,
),
ScsOption(
"--bucketfs-name",
metavar="BFS_SERVICE",
type=str,
default="bfsdefault",
help="BucketFS service name",
scs_key=CKey.bfs_service,
),
ScsOption(
"--bucket",
metavar="BUCKET",
type=str,
default="default",
help="BucketFS bucket name",
scs_key=CKey.bfs_bucket,
),
ScsOption(
"--bucketfs-use-encryption/--no-bucketfs-use-encryption",
type=bool,
default=True,
help="Whether to encrypt communication with the BucketFS or not",
scs_key=CKey.bfs_encryption,
),
]
35 changes: 35 additions & 0 deletions exasol/nb_connector/cli/options/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pathlib import Path
Copy link
Collaborator

@ahsimb ahsimb Sep 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the name of the file a bit confusing. Would "common.py" be a better name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, definitely.
I will rename the file.


import click

from exasol.nb_connector.cli.param_wrappers import (
ScsArgument,
ScsOption,
)

SCS_OPTIONS = [
ScsArgument(
"scs_file",
metavar="SCS_FILE",
type=Path,
required=True,
envvar="SCS_FILE",
),
]

SAVE_OPTIONS = [
ScsOption(
"--overwrite-backend/--no-overwrite-backend",
is_flag=True,
help="Whether to overwrite a different backend in the SCS.",
)
]

COMMON_OPTIONS = [
ScsOption(
"--db-schema",
metavar="DB_SCHEMA",
type=str,
help="Database schema for installing UDFs of Exasol extensions",
)
]
31 changes: 31 additions & 0 deletions exasol/nb_connector/cli/options/docker_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import click

from exasol.nb_connector.ai_lab_config import Accelerator
from exasol.nb_connector.ai_lab_config import AILabConfig as CKey
from exasol.nb_connector.cli.param_wrappers import ScsOption

EXTRA_DOCKER_DB_OPTIONS = [
ScsOption(
"--db-mem-size",
type=int,
metavar="GiB",
default=2,
help="Database memory size (GiB)",
scs_key=CKey.mem_size,
),
ScsOption(
"--db-disk-size",
metavar="GiB",
type=int,
default=2,
help="Database disk size (GiB)",
scs_key=CKey.disk_size,
),
ScsOption(
"--accelerator",
type=click.Choice(Accelerator, case_sensitive=False),
default="none",
help="Hardware acceleration",
scs_key=CKey.accelerator,
),
]
44 changes: 44 additions & 0 deletions exasol/nb_connector/cli/options/onprem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from exasol.nb_connector.ai_lab_config import AILabConfig as CKey
from exasol.nb_connector.cli.param_wrappers import (
ScsOption,
ScsSecretOption,
)

ONPREM_DB_OPTIONS = [
ScsOption(
"--db-host-name",
metavar="HOST",
type=str,
default="localhost",
help="Database connection host name",
scs_key=CKey.db_host_name,
),
ScsOption(
"--db-port",
metavar="PORT",
type=int,
default=8563,
help="Database connection port",
scs_key=CKey.db_port,
),
ScsOption(
"--db-username",
metavar="USERNAME",
type=str,
help="Database user name",
scs_key=CKey.db_user,
),
ScsSecretOption(
"--db-password",
envvar="SCS_EXASOL_DB_PASSWORD",
prompt="Exasol database password",
scs_key=CKey.db_password,
),
ScsOption(
"--db-use-encryption/--no-db-use-encryption",
type=bool,
default=True,
help="Whether to encrypt communication with the database or not",
scs_key=CKey.db_encryption,
),
]
49 changes: 49 additions & 0 deletions exasol/nb_connector/cli/options/saas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from exasol.nb_connector.ai_lab_config import AILabConfig as CKey
from exasol.nb_connector.cli.param_wrappers import (
ScsOption,
ScsSecretOption,
)

EXTRA_SAAS_OPTIONS = [
ScsOption(
"--saas-url",
metavar="URL",
type=str,
default="https://cloud.exasol.com",
help="Exasol SaaS service URL",
scs_key=CKey.saas_url,
),
ScsOption(
"--saas-account-id",
metavar="ACCOUNT_ID",
type=str,
help="Exasol SaaS account ID",
scs_key=CKey.saas_account_id,
),
# CKey.saas_database_id and CKey.saas_database_name can be used
# alternatively, see
# https://github.com/exasol/saas-api-python/blob/main/exasol/saas/client/api_access.py#L134
ScsOption(
"--saas-database-id",
metavar="ID",
type=str,
help="Exasol SaaS database ID",
scs_key=CKey.saas_database_id,
scs_alternative_key=CKey.saas_database_name,
),
ScsOption(
"--saas-database-name",
metavar="NAME",
type=str,
help="Exasol SaaS database name",
scs_key=CKey.saas_database_name,
scs_alternative_key=CKey.saas_database_id,
),
ScsSecretOption(
"--saas-token",
envvar="SCS_EXASOL_SAAS_TOKEN",
prompt="Exasol SaaS personal access token (PAT)",
scs_key=CKey.saas_token,
metavar="PAT",
),
]
Loading