-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
332be3f
commit 719a42c
Showing
12 changed files
with
447 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,77 @@ | ||
import rich_click as click | ||
|
||
from typing import cast | ||
|
||
from rich_click.utils import OptionGroupDict | ||
|
||
from armonik_cli import commands, __version__ | ||
from armonik_cli.core import Configuration, console | ||
|
||
|
||
COMMON_OPTIONS = cast( | ||
OptionGroupDict, {"name": "Common options", "options": ["--debug", "--output", "--help"]} | ||
) | ||
CONNECTION_OPTIONS = cast( | ||
OptionGroupDict, {"name": "Connection options", "options": ["--endpoint"]} | ||
) | ||
click.rich_click.OPTION_GROUPS = { | ||
"armonik": [ | ||
{ | ||
"name": "Common options", | ||
"options": ["--version", "--help"], | ||
} | ||
], | ||
"armonik session create": [ | ||
{ | ||
"name": "Session configuration options", | ||
"options": [ | ||
"--max-duration", | ||
"--max-retries", | ||
"--priority", | ||
"--partition", | ||
"--default-partition", | ||
"--application-name", | ||
"--application-version", | ||
"--application-namespace", | ||
"--application-service", | ||
"--engine-type", | ||
"--option", | ||
], | ||
}, | ||
CONNECTION_OPTIONS, | ||
COMMON_OPTIONS, | ||
], | ||
"armonik session list": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session get": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session pause": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session resume": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session delete": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session cancel": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session purge": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session close": [CONNECTION_OPTIONS, COMMON_OPTIONS], | ||
"armonik session stop-submission": [ | ||
{ | ||
"name": "Submission interruption options", | ||
"options": ["--clients-only", "--workers-only"], | ||
}, | ||
CONNECTION_OPTIONS, | ||
COMMON_OPTIONS, | ||
], | ||
} | ||
|
||
|
||
@click.group(name="armonik") | ||
@click.version_option(version=__version__, prog_name="armonik") | ||
def cli() -> None: | ||
@click.pass_context | ||
def cli(ctx: click.Context) -> None: | ||
""" | ||
ArmoniK CLI is a tool to monitor and manage ArmoniK clusters. | ||
""" | ||
pass | ||
if "help" not in ctx.args: | ||
if not Configuration.default_path.exists(): | ||
console.print(f"Created configuration file at {Configuration.default_path}") | ||
Configuration.create_default_if_not_exists() | ||
|
||
|
||
cli.add_command(commands.sessions) | ||
cli.add_command(commands.session) | ||
cli.add_command(commands.config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
from .sessions import sessions | ||
from armonik_cli.commands.config import config | ||
from armonik_cli.commands.session import session | ||
|
||
|
||
__all__ = ["sessions"] | ||
__all__ = ["config", "session"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import json | ||
|
||
import rich_click as click | ||
|
||
from pathlib import Path | ||
|
||
from rich.prompt import Prompt | ||
|
||
from armonik_cli.core import console, Configuration, MutuallyExclusiveOption, base_command | ||
|
||
|
||
key_argument = click.argument("key", required=True, type=str, metavar="KEY") | ||
|
||
|
||
@click.group() | ||
def config(): | ||
"""Display or change configuration settings for ArmoniK CLI.""" | ||
pass | ||
|
||
|
||
@config.command() | ||
@key_argument | ||
@base_command(connection_args=False) | ||
def get(key: str, output: str, debug: bool) -> None: | ||
"""Retrieve the value of a configuration setting by its KEY.""" | ||
config = Configuration.load_default() | ||
if config.has(key): | ||
return console.formatted_print({key: config.get(key)}, format=output) | ||
return console.print(f"Warning: '{key}' is not a known configuration key.") | ||
|
||
|
||
# The function cannot be called 'set' directly, as this causes a conflict with the constructor of the built-in set object. | ||
@config.command("set") | ||
@key_argument | ||
@click.argument("value", required=True, type=str, metavar="VALUE") | ||
@base_command(connection_args=False) | ||
def set_(key: str, value: str, output: str, debug: bool) -> None: | ||
"""Update a configuration setting with a VALUE for the given KEY.""" | ||
config = Configuration.load_default() | ||
if config.has(key): | ||
config.set(key, value) | ||
return console.print(f"Updated '{key}' configuration with value '{value}'.") | ||
return console.print(f"Warning: '{key}' is not a known configuration key.") | ||
|
||
|
||
@config.command() | ||
@base_command(connection_args=False) | ||
def list(output: str, debug: bool) -> None: | ||
"""Display all configuration settings.""" | ||
config = Configuration.load_default() | ||
console.formatted_print(config.to_dict(), format=output) | ||
|
||
|
||
@config.command() | ||
@click.option( | ||
"--local", | ||
is_flag=True, | ||
help="Set to a local cluster without TLS enabled.", | ||
cls=MutuallyExclusiveOption, | ||
mutual=["interactive", "source"], | ||
) | ||
@click.option( | ||
"--interactive", | ||
is_flag=True, | ||
help="Use interactive prompts.", | ||
cls=MutuallyExclusiveOption, | ||
mutual=["local", "source"], | ||
) | ||
@click.option( | ||
"--source", | ||
type=click.Path(exists=True, file_okay=False, dir_okay=True), | ||
help="Use the deployment generated folder to retrieve connection details.", | ||
cls=MutuallyExclusiveOption, | ||
mutual=["local", "interactive"], | ||
) | ||
@base_command(connection_args=False) | ||
def set_connection(local: bool, interactive: bool, source: str, output: str, debug: bool) -> None: | ||
"""Update all cluster connection configuration settings at once.""" | ||
endpoint = "" | ||
if local: | ||
endpoint = "172.17.119.85:5001" | ||
elif interactive: | ||
endpoint = Prompt.get_input(console, "Endpoint: ", password=False) | ||
elif source: | ||
with (Path(source) / "armonik-output.json").open() as output_file: | ||
endpoint = json.loads(output_file.read())["armonik"]["control_plane_url"] | ||
else: | ||
return | ||
config = Configuration.load_default() | ||
config.set("endpoint", endpoint) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,15 @@ | ||
from armonik_cli.core.config import Configuration | ||
from armonik_cli.core.console import console | ||
from armonik_cli.core.decorators import base_command | ||
from armonik_cli.core.options import MutuallyExclusiveOption | ||
from armonik_cli.core.params import KeyValuePairParam, TimeDeltaParam | ||
|
||
|
||
__all__ = ["base_command", "KeyValuePairParam", "TimeDeltaParam", "console"] | ||
__all__ = [ | ||
"base_command", | ||
"KeyValuePairParam", | ||
"TimeDeltaParam", | ||
"console", | ||
"Configuration", | ||
"MutuallyExclusiveOption", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import json | ||
import rich_click as click | ||
|
||
from pathlib import Path | ||
from typing import Union, Dict | ||
|
||
|
||
class Configuration: | ||
""" | ||
A class to manage application configuration settings, providing methods | ||
for loading, creating, updating, and saving the configuration. | ||
Attributes: | ||
default_path: The default path to the configuration file. | ||
_config_keys: The list of valid configuration keys. | ||
_default_config: The default configuration values. | ||
""" | ||
|
||
default_path = Path(click.get_app_dir("armonik_cli")) / "config" | ||
_config_keys = ["endpoint"] | ||
_default_config = {"endpoint": None} | ||
|
||
def __init__(self, endpoint: str) -> None: | ||
self.endpoint = endpoint | ||
|
||
@classmethod | ||
def create_default_if_not_exists(cls) -> None: | ||
""" | ||
Create a default configuration file if it does not already exist. | ||
This method creates the configuration directory if needed, and writes | ||
the default configuration to a file if it is not already present. | ||
""" | ||
cls.default_path.parent.mkdir(exist_ok=True) | ||
if not (cls.default_path.is_file() and cls.default_path.exists()): | ||
with cls.default_path.open("w") as config_file: | ||
config_file.write(json.dumps(cls._default_config, indent=4)) | ||
|
||
@classmethod | ||
def load_default(cls) -> "Configuration": | ||
""" | ||
Load the configuration from the default configuration file. | ||
Returns: | ||
An instance of Configuration populated with values from the default file. | ||
""" | ||
with cls.default_path.open("r") as config_file: | ||
return cls(**json.loads(config_file.read())) | ||
|
||
def has(self, key: str) -> bool: | ||
""" | ||
Check if a specified configuration key is valid. | ||
Args: | ||
key: The configuration key to check. | ||
Returns: | ||
True if the key is valid, False otherwise. | ||
""" | ||
return key in self._config_keys | ||
|
||
def get(self, key: str) -> Union[str, None]: | ||
""" | ||
Retrieve the value of a specified configuration key. | ||
Args: | ||
key: The configuration key to retrieve. | ||
Returns: | ||
The value of the configuration key, or None if the key is invalid. | ||
""" | ||
if self.has(key): | ||
return getattr(self, key) | ||
return None | ||
|
||
def set(self, key: str, value: str) -> None: | ||
""" | ||
Set the value of a specified configuration key and save the configuration. | ||
Args: | ||
key: The configuration key to set. | ||
value: The value to assign to the configuration key. | ||
""" | ||
if self.has(key): | ||
setattr(self, key, value) | ||
self._save() | ||
|
||
def to_dict(self) -> Dict[str, str]: | ||
""" | ||
Convert the configuration to a dictionary format. | ||
Returns: | ||
A dictionary representation of the configuration. | ||
""" | ||
return {key: getattr(self, key) for key in self._config_keys} | ||
|
||
def _save(self): | ||
""" | ||
Save the current configuration values to the default configuration file. | ||
""" | ||
with self.default_path.open("w") as config_file: | ||
config_file.write(json.dumps(self.to_dict(), indent=4)) |
Oops, something went wrong.