diff --git a/cmapi/mcs_cluster_tool/__main__.py b/cmapi/mcs_cluster_tool/__main__.py index b7bfdbadf..4c15284b6 100644 --- a/cmapi/mcs_cluster_tool/__main__.py +++ b/cmapi/mcs_cluster_tool/__main__.py @@ -47,6 +47,9 @@ app.command( 'cspasswd', rich_help_panel='Tools commands', short_help='Encrypt a Columnstore plaintext password.' )(tools_commands.cspasswd) +app.command( + 'bootstrap-single-node', rich_help_panel='Tools commands', +)(tools_commands.bootstrap_single_node) @app.command( diff --git a/cmapi/mcs_cluster_tool/cluster_app.py b/cmapi/mcs_cluster_tool/cluster_app.py index 2b76728e4..d93c8cc2c 100644 --- a/cmapi/mcs_cluster_tool/cluster_app.py +++ b/cmapi/mcs_cluster_tool/cluster_app.py @@ -14,6 +14,7 @@ from typing_extensions import Annotated from cmapi_server.constants import ( CMAPI_CONF_PATH, DEFAULT_MCS_CONF_PATH, REQUEST_TIMEOUT ) +from cmapi_server.controllers.api_clients import ClusterControllerClient from cmapi_server.exceptions import CMAPIBasicError from cmapi_server.helpers import ( get_config_parser, get_current_key, get_version, build_url @@ -21,7 +22,6 @@ from cmapi_server.helpers import ( from cmapi_server.managers.transaction import TransactionManager from mcs_cluster_tool.decorators import handle_output from mcs_node_control.models.node_config import NodeConfig -from cmapi_server.controllers.api_clients import ClusterControllerClient logger = logging.getLogger('mcs_cli') diff --git a/cmapi/mcs_cluster_tool/tools_commands.py b/cmapi/mcs_cluster_tool/tools_commands.py index 0e4377688..3d3ddc0dc 100644 --- a/cmapi/mcs_cluster_tool/tools_commands.py +++ b/cmapi/mcs_cluster_tool/tools_commands.py @@ -1,15 +1,19 @@ import logging import os +import secrets +from datetime import datetime, timedelta import typer from typing_extensions import Annotated from cmapi_server.constants import ( - MCS_DATA_PATH, MCS_SECRETS_FILENAME + MCS_DATA_PATH, MCS_SECRETS_FILENAME, REQUEST_TIMEOUT, TRANSACTION_TIMEOUT, ) +from cmapi_server.controllers.api_clients import ClusterControllerClient from cmapi_server.exceptions import CEJError from cmapi_server.handlers.cej import CEJPasswordHandler +from cmapi_server.managers.transaction import TransactionManager from mcs_cluster_tool.decorators import handle_output @@ -112,4 +116,37 @@ def cspasswd( typer.echo(cej_error.message, color='red') raise typer.Exit(code=1) typer.echo(f'Encoded password: {encoded_password}', color='green') - raise typer.Exit(code=0) \ No newline at end of file + raise typer.Exit(code=0) + + +@handle_output +def bootstrap_single_node( + key: Annotated[ + str, + typer.Option( + '--api-key', + help='API key to set.', + ) + ] = '' +): + """Bootstrap a single node (localhost) Columnstore instance.""" + node = 'localhost' + client = ClusterControllerClient(request_timeout=REQUEST_TIMEOUT) + if not key: + # Generate API key if not provided + key = secrets.token_urlsafe(32) + # handle_output decorator will catch, show and log errors + api_key_set_resp = client.set_api_key(key) + # if operation takes minutes, then it is better to raise by timeout + with TransactionManager( + timeout=TRANSACTION_TIMEOUT, handle_signals=True, + extra_nodes=[node] + ): + add_node_resp = client.add_node({'node': node}) + + result = { + 'timestamp': str(datetime.now()), + 'set_api_key_resp': api_key_set_resp, + 'add_node_resp': add_node_resp, + } + return result