diff --git a/kernelci/cli/__init__.py b/kernelci/cli/__init__.py index 22e4bc24ab..fd637c02fa 100644 --- a/kernelci/cli/__init__.py +++ b/kernelci/cli/__init__.py @@ -21,6 +21,9 @@ import click import requests +import kernelci.api +import kernelci.api.helper +import kernelci.config import kernelci.settings @@ -240,3 +243,24 @@ def get_pagination(page_length: int, page_number: int): f"Page number must be at least 0, got {page_number}" ) return page_number * page_length, page_length + + +def get_api(config, api, + secrets: typing.Optional[kernelci.settings.Secrets] = None): + """Get an API object instance + + Return an API object based on the given `api` config name loaded from the + YAML config found in the `config` path or in the `config` dictionary + already loaded. + """ + if not isinstance(config, dict): + config = kernelci.config.load(config) + api_config = config['api'][api] + token = secrets.api.token if secrets else None + return kernelci.api.get_api(api_config, token) + + +def get_api_helper(*args, **kwargs): + """Wrapper around get_api() to get an APIHelper object""" + api = get_api(*args, **kwargs) + return kernelci.api.helper.APIHelper(api) diff --git a/kernelci/cli/api.py b/kernelci/cli/api.py index 292d6758f5..7f2c468b50 100644 --- a/kernelci/cli/api.py +++ b/kernelci/cli/api.py @@ -9,9 +9,11 @@ import click -import kernelci.api -import kernelci.config -from . import Args, kci +from . import ( + Args, + get_api, + kci, +) @kci.group(name='api') @@ -23,10 +25,8 @@ def kci_api(): @Args.config @Args.api @Args.indent -def hello(config, indent, api): +def hello(config, api, indent): """Query the API root endpoint""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) data = api.hello() click.echo(json.dumps(data, indent=indent or None)) diff --git a/kernelci/cli/event.py b/kernelci/cli/event.py index 5368e88398..12541273bc 100644 --- a/kernelci/cli/event.py +++ b/kernelci/cli/event.py @@ -11,10 +11,12 @@ import click -import kernelci.api -import kernelci.api.helper -import kernelci.config -from . import Args, kci +from . import ( + Args, + get_api, + get_api_helper, + kci, +) @kci.group(name='event') @@ -28,9 +30,7 @@ def kci_event(): @Args.api def subscribe(config, api, channel, secrets): """Subscribe to a Pub/Sub channel""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) sub_id = api.subscribe(channel) click.echo(sub_id) @@ -41,9 +41,7 @@ def subscribe(config, api, channel, secrets): @Args.api def unsubscribe(config, api, sub_id, secrets): """Unsubscribe from a Pub/Sub channel""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) api.unsubscribe(sub_id) @@ -54,9 +52,7 @@ def unsubscribe(config, api, sub_id, secrets): @click.argument('channel') def send(config, api, is_json, channel, secrets): """Read some data on stdin and send it as an event on a channel""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) data = sys.stdin.read() if is_json: data = json.loads(data) @@ -70,10 +66,7 @@ def send(config, api, is_json, channel, secrets): @Args.indent def receive(config, api, indent, sub_id, secrets): """Wait and receive an event from a subscription and print on stdout""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) - helper = kernelci.api.helper.APIHelper(api) + helper = get_api_helper(config, api, secrets) event = helper.receive_event_data(sub_id) if isinstance(event, str): click.echo(event.strip()) @@ -90,9 +83,7 @@ def receive(config, api, indent, sub_id, secrets): @click.argument('list_name') def push(config, api, is_json, list_name, secrets): """Read some data on stdin and push it as an event on a list""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) data = sys.stdin.read() if is_json: data = json.loads(data) @@ -106,10 +97,7 @@ def push(config, api, is_json, list_name, secrets): @Args.indent def pop(config, api, indent, list_name, secrets): """Wait and pop an event from a List when received print on stdout""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) - helper = kernelci.api.helper.APIHelper(api) + helper = get_api_helper(config, api, secrets) event = helper.pop_event_data(list_name) if isinstance(event, str): click.echo(event.strip()) diff --git a/kernelci/cli/job.py b/kernelci/cli/job.py index 97a62778f2..99e2fe04b0 100644 --- a/kernelci/cli/job.py +++ b/kernelci/cli/job.py @@ -10,11 +10,15 @@ import click -import kernelci.api import kernelci.config -import kernelci.api.helper import kernelci.runtime -from . import Args, kci, catch_http_error +from . import ( + Args, + catch_http_error, + get_api, + get_api_helper, + kci, +) @kci.group(name='job') @@ -34,11 +38,9 @@ def new(name, node_id, node_json, config, # pylint: disable=too-many-arguments api, indent, secrets): """Create a new job node""" configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) - helper = kernelci.api.helper.APIHelper(api) + helper = get_api_helper(configs, api, secrets) if node_id: - input_node = api.get_node(node_id) + input_node = helper.api.get_node(node_id) elif node_json: input_node = helper.load_json(node_json) else: @@ -69,8 +71,7 @@ def generate(node_id, # pylint: disable=too-many-arguments, too-many-locals runtime, storage, platform, output, config, api, secrets): """Generate a job definition in a file""" configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(configs, api, secrets) job_node = api.get_node(node_id) job = kernelci.runtime.Job(job_node, configs['jobs'][job_node['name']]) job.platform_config = configs['device_types'][platform] diff --git a/kernelci/cli/node.py b/kernelci/cli/node.py index 8296799fa0..ba9c83b970 100644 --- a/kernelci/cli/node.py +++ b/kernelci/cli/node.py @@ -11,9 +11,12 @@ import click -import kernelci.api -import kernelci.config -from . import Args, kci, split_attributes +from . import ( + Args, + get_api, + kci, + split_attributes, +) @kci.group(name='node') @@ -28,9 +31,7 @@ def kci_node(): @Args.indent def get(node_id, config, api, indent): """Get a node with a given ID""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) node = api.get_node(node_id) click.echo(json.dumps(node, indent=indent)) @@ -48,10 +49,9 @@ def get(node_id, config, api, indent): def find(attributes, config, api, # pylint: disable=too-many-arguments indent, offset, limit): """Find nodes with arbitrary attributes""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) - nodes = api.get_nodes(split_attributes(attributes), offset, limit) + api = get_api(config, api) + attributes = split_attributes(attributes) + nodes = api.get_nodes(attributes, offset, limit) data = json.dumps(nodes, indent=indent) echo = click.echo_via_pager if len(nodes) > 1 else click.echo echo(data) @@ -63,10 +63,9 @@ def find(attributes, config, api, # pylint: disable=too-many-arguments @Args.api def count(attributes, config, api): """Count nodes with arbitrary attributes""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) - click.echo(api.count_nodes(split_attributes(attributes))) + api = get_api(config, api) + attributes = split_attributes(attributes) + click.echo(api.count_nodes(attributes)) @kci_node.command(secrets=True) @@ -75,9 +74,7 @@ def count(attributes, config, api): @Args.indent def submit(config, api, secrets, indent): """Submit a new node or update an existing one from stdin""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) data = json.load(sys.stdin) if 'id' in data: node = api.update_node(data) diff --git a/kernelci/cli/user.py b/kernelci/cli/user.py index 9267d92bdd..0ba48cf4b3 100644 --- a/kernelci/cli/user.py +++ b/kernelci/cli/user.py @@ -12,9 +12,13 @@ import click -import kernelci.api -import kernelci.config -from . import Args, kci, split_attributes, catch_http_error +from . import ( + Args, + catch_http_error, + get_api, + kci, + split_attributes, +) @kci.group(name='user') @@ -29,9 +33,7 @@ def kci_user(): @catch_http_error def whoami(config, api, indent, secrets): """Get the current user's details with API authentication""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) data = api.whoami() click.echo(json.dumps(data, indent=indent)) @@ -44,10 +46,9 @@ def whoami(config, api, indent, secrets): @catch_http_error def find(attributes, config, api, indent, secrets): """Find user profiles with arbitrary attributes""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) - users = api.get_users(split_attributes(attributes)) + api = get_api(config, api, secrets) + attributes = split_attributes(attributes) + users = api.get_users(attributes) data = json.dumps(users, indent=indent) echo = click.echo_via_pager if len(users) > 1 else click.echo echo(data) @@ -60,9 +61,7 @@ def find(attributes, config, api, indent, secrets): @catch_http_error def token(username, config, api): """Create a new API token using a user name and password""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) password = getpass.getpass() api_token = api.create_token(username, password) click.echo(api_token['access_token']) @@ -81,9 +80,7 @@ def update(attributes, username, config, api, secrets): click.echo("No user details to update.") return - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) if username: users = api.get_users({"username": username}) if not users: @@ -111,9 +108,7 @@ def add(username, email, config, api, secrets): 'email': email, 'password': password, } - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) api.create_user(user) @@ -124,9 +119,7 @@ def add(username, email, config, api, secrets): @catch_http_error def verify(username, config, api): """Verify the user's email address""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) users = api.get_users({"username": username}) if not users: raise click.ClickException(f"User not found: {username}") @@ -147,9 +140,7 @@ def verify(username, config, api): @catch_http_error def get(user_id, config, api, indent): """Get a user with a given ID""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) user = api.get_user(user_id) click.echo(json.dumps(user, indent=indent)) @@ -161,9 +152,7 @@ def get(user_id, config, api, indent): @catch_http_error def activate(username, config, api, secrets): """Activate user account (admin only)""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) users = api.get_users({"username": username}) if not users: raise click.ClickException(f"User not found: {username}") @@ -178,9 +167,7 @@ def activate(username, config, api, secrets): @catch_http_error def deactivate(username, config, api, secrets): """Deactivate a user account (admin only)""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) users = api.get_users({"username": username}) if not users: raise click.ClickException(f"User not found: {username}") @@ -200,9 +187,7 @@ def user_password(): @catch_http_error def password_update(username, config, api): """Update password for a user account""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) current_password = getpass.getpass("Current password: ") new_password = getpass.getpass("New password: ") retyped = getpass.getpass("Retype new password: ") @@ -218,9 +203,7 @@ def password_update(username, config, api): @catch_http_error def password_reset(username, config, api): """Reset password for a user account""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) + api = get_api(config, api) users = api.get_users({"username": username}) if not users: raise click.ClickException(f"User not found: {username}") @@ -249,10 +232,9 @@ def user_group(): @catch_http_error def find_groups(attributes, config, api, indent): """Find user groups with arbitrary attributes""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config) - users = api.get_groups(split_attributes(attributes)) + api = get_api(config, api) + attributes = split_attributes(attributes) + users = api.get_groups(attributes) data = json.dumps(users, indent=indent) echo = click.echo_via_pager if len(users) > 1 else click.echo echo(data) @@ -265,9 +247,7 @@ def find_groups(attributes, config, api, indent): @catch_http_error def group_add(name, config, api, secrets): """Create a new group""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) api.create_group(name) @@ -279,9 +259,7 @@ def group_add(name, config, api, secrets): @catch_http_error def join(name, username, config, api, secrets): """Add a user to a group (admin only)""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) users = api.get_users({"username": username}) if not users: raise click.ClickException(f"User not found: {username}") @@ -299,9 +277,7 @@ def join(name, username, config, api, secrets): @catch_http_error def leave(name, config, api, secrets): """Leave a user group""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) user = api.whoami() groups = [] for group in user['groups']: @@ -318,9 +294,7 @@ def leave(name, config, api, secrets): @catch_http_error def delete(name, config, api, secrets): """Delete a user group (admin only)""" - configs = kernelci.config.load(config) - api_config = configs['api'][api] - api = kernelci.api.get_api(api_config, secrets.api.token) + api = get_api(config, api, secrets) groups = api.get_groups({"name": name}) if not groups: raise click.ClickException(f"Group not found: {name}") diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2