thestage 0.5.471__py3-none-any.whl → 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thestage/.env +5 -5
- thestage/__init__.py +3 -4
- thestage/__main__.py +9 -9
- thestage/cli_command.py +56 -56
- thestage/cli_command_helper.py +51 -51
- thestage/color_scheme/color_scheme.py +7 -7
- thestage/config/__init__.py +18 -18
- thestage/config/config_storage.py +5 -5
- thestage/config/env_base.py +7 -7
- thestage/controllers/__init__.py +0 -0
- thestage/controllers/base_controller.py +67 -67
- thestage/controllers/config_controller.py +137 -137
- thestage/controllers/container_controller.py +389 -389
- thestage/controllers/instance_controller.py +183 -183
- thestage/controllers/project_controller.py +810 -783
- thestage/controllers/utils_controller.py +32 -32
- thestage/debug_main.dist.py +28 -28
- thestage/entities/__init__.py +0 -0
- thestage/entities/container.py +17 -17
- thestage/entities/enums/__init__.py +0 -0
- thestage/entities/enums/order_direction_type.py +6 -6
- thestage/entities/enums/shell_type.py +7 -7
- thestage/entities/enums/tail_output_type.py +6 -6
- thestage/entities/enums/yes_no_response.py +7 -7
- thestage/entities/file_item.py +27 -27
- thestage/entities/project_inference_simulator.py +18 -18
- thestage/entities/project_inference_simulator_model.py +16 -16
- thestage/entities/project_task.py +19 -19
- thestage/entities/rented_instance.py +19 -19
- thestage/entities/self_hosted_instance.py +18 -18
- thestage/exceptions/__init__.py +0 -0
- thestage/exceptions/auth_exception.py +6 -6
- thestage/exceptions/base_exception.py +13 -13
- thestage/exceptions/business_logic_exception.py +6 -6
- thestage/exceptions/config_exception.py +6 -6
- thestage/exceptions/file_system_exception.py +6 -6
- thestage/exceptions/git_access_exception.py +17 -17
- thestage/exceptions/remote_server_exception.py +24 -24
- thestage/git/ProgressPrinter.py +22 -22
- thestage/helpers/__init__.py +0 -0
- thestage/helpers/error_handler.py +115 -115
- thestage/helpers/exception_hook.py +14 -14
- thestage/helpers/logger/__init__.py +0 -0
- thestage/helpers/logger/app_logger.py +50 -50
- thestage/helpers/ssh_util.py +38 -38
- thestage/i18n/en_GB/messages.po +947 -947
- thestage/i18n/translation.py +9 -9
- thestage/main.py +36 -36
- thestage/services/.env +6 -6
- thestage/services/__init__.py +0 -0
- thestage/services/abstract_mapper.py +9 -9
- thestage/services/abstract_service.py +87 -87
- thestage/services/app_config_service.py +52 -52
- thestage/services/clients/__init__.py +0 -0
- thestage/services/clients/git/__init__.py +0 -0
- thestage/services/clients/git/git_client.py +436 -331
- thestage/services/clients/thestage_api/__init__.py +0 -0
- thestage/services/clients/thestage_api/api_client.py +718 -720
- thestage/services/clients/thestage_api/core/api_client_core.py +108 -108
- thestage/services/clients/thestage_api/core/http_client_exception.py +12 -12
- thestage/services/clients/thestage_api/dtos/__init__.py +0 -0
- thestage/services/clients/thestage_api/dtos/base_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/cloud_provider_region.py +19 -19
- thestage/services/clients/thestage_api/dtos/container_param_request.py +11 -11
- thestage/services/clients/thestage_api/dtos/container_response.py +67 -67
- thestage/services/clients/thestage_api/dtos/docker_container_assigned_device.py +10 -10
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/docker_container_mapping.py +10 -10
- thestage/services/clients/thestage_api/dtos/entity_filter_request.py +14 -14
- thestage/services/clients/thestage_api/dtos/enums/__init__.py +0 -0
- thestage/services/clients/thestage_api/dtos/enums/container_pending_action.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/container_status.py +17 -17
- thestage/services/clients/thestage_api/dtos/enums/cpu_type.py +8 -8
- thestage/services/clients/thestage_api/dtos/enums/currency_type.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/daemon_status.py +9 -9
- thestage/services/clients/thestage_api/dtos/enums/disk_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/drive_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/gpu_name.py +8 -8
- thestage/services/clients/thestage_api/dtos/enums/inference_model_status.py +9 -9
- thestage/services/clients/thestage_api/dtos/enums/inference_simulator_status.py +15 -15
- thestage/services/clients/thestage_api/dtos/enums/instance_rented_status.py +17 -17
- thestage/services/clients/thestage_api/dtos/enums/instance_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/location_region.py +11 -11
- thestage/services/clients/thestage_api/dtos/enums/power_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/provider_name.py +11 -11
- thestage/services/clients/thestage_api/dtos/enums/selfhosted_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/task_execution_status.py +12 -12
- thestage/services/clients/thestage_api/dtos/enums/task_status.py +12 -12
- thestage/services/clients/thestage_api/dtos/frontend_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +10 -10
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_request.py +14 -14
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/installed_service.py +17 -17
- thestage/services/clients/thestage_api/dtos/instance_detected_gpus.py +20 -20
- thestage/services/clients/thestage_api/dtos/instance_rented_response.py +71 -71
- thestage/services/clients/thestage_api/dtos/logging_controller/docker_container_log_stream_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_response.py +14 -14
- thestage/services/clients/thestage_api/dtos/logging_controller/task_log_stream_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +21 -21
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_response.py +14 -14
- thestage/services/clients/thestage_api/dtos/paginated_entity_list.py +11 -11
- thestage/services/clients/thestage_api/dtos/pagination_data.py +10 -10
- thestage/services/clients/thestage_api/dtos/price_definition.py +14 -14
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_response.py +6 -6
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +15 -15
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +13 -14
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_response.py +32 -32
- thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +56 -56
- thestage/services/clients/thestage_api/dtos/sftp_path_helper.py +13 -13
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +10 -10
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +9 -9
- thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/user_controller/user_profile.py +12 -12
- thestage/services/clients/thestage_api/dtos/validate_token_response.py +11 -11
- thestage/services/config_provider/__init__.py +0 -0
- thestage/services/config_provider/config_provider.py +237 -237
- thestage/services/connect/connect_service.py +196 -196
- thestage/services/connect/dto/remote_server_config.py +9 -9
- thestage/services/container/__init__.py +0 -0
- thestage/services/container/container_service.py +374 -374
- thestage/services/container/mapper/__init__.py +0 -0
- thestage/services/container/mapper/container_mapper.py +30 -30
- thestage/services/core_files/config_entity.py +26 -26
- thestage/services/filesystem_service.py +133 -133
- thestage/services/instance/__init__.py +0 -0
- thestage/services/instance/instance_service.py +303 -303
- thestage/services/instance/mapper/__init__.py +0 -0
- thestage/services/instance/mapper/instance_mapper.py +24 -24
- thestage/services/instance/mapper/selfhosted_mapper.py +33 -33
- thestage/services/logging/byte_print_style.py +5 -5
- thestage/services/logging/dto/log_message.py +15 -15
- thestage/services/logging/dto/log_type.py +6 -6
- thestage/services/logging/exception/log_polling_exception.py +6 -6
- thestage/services/logging/logging_constants.py +3 -3
- thestage/services/logging/logging_service.py +367 -367
- thestage/services/project/__init__.py +0 -0
- thestage/services/project/dto/inference_simulator_dto.py +22 -22
- thestage/services/project/dto/inference_simulator_model_dto.py +20 -20
- thestage/services/project/dto/project_config.py +14 -14
- thestage/services/project/mapper/__init__.py +0 -0
- thestage/services/project/mapper/project_inference_simulator_mapper.py +21 -21
- thestage/services/project/mapper/project_inference_simulator_model_mapper.py +21 -21
- thestage/services/project/mapper/project_task_mapper.py +22 -22
- thestage/services/project/project_service.py +1253 -1241
- thestage/services/remote_server_service.py +609 -609
- thestage/services/service_factory.py +97 -97
- thestage/services/task/dto/task_dto.py +40 -40
- thestage/services/validation_service.py +61 -61
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/LICENSE.txt +12 -12
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/METADATA +1 -1
- thestage-0.6.2.dist-info/RECORD +176 -0
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/WHEEL +1 -1
- thestage/debug_tests.py +0 -12
- thestage/services/clients/.DS_Store +0 -0
- thestage-0.5.471.dist-info/RECORD +0 -178
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
import typer
|
|
3
|
-
|
|
4
|
-
from thestage.cli_command import CliCommand
|
|
5
|
-
from thestage.cli_command_helper import check_command_permission
|
|
6
|
-
from thestage.i18n.translation import __
|
|
7
|
-
from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
|
|
8
|
-
from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
|
|
9
|
-
from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
|
|
10
|
-
from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
|
|
11
|
-
from thestage.services.abstract_service import AbstractService
|
|
12
|
-
from thestage.helpers.error_handler import error_handler
|
|
13
|
-
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
14
|
-
from thestage.services.clients.thestage_api.dtos.enums.task_status import TaskStatus
|
|
15
|
-
from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
|
|
16
|
-
from thestage.services.container.container_service import ContainerService
|
|
17
|
-
from thestage.services.instance.instance_service import InstanceService
|
|
18
|
-
from thestage.services.logging.logging_service import LoggingService
|
|
19
|
-
from thestage.services.task.dto.task_dto import TaskDto
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ConnectService(AbstractService):
|
|
23
|
-
__thestage_api_client: TheStageApiClient = None
|
|
24
|
-
__instance_service: InstanceService = None
|
|
25
|
-
__container_service: ContainerService = None
|
|
26
|
-
__logging_service: LoggingService = None
|
|
27
|
-
|
|
28
|
-
def __init__(
|
|
29
|
-
self,
|
|
30
|
-
thestage_api_client: TheStageApiClient,
|
|
31
|
-
instance_service: InstanceService,
|
|
32
|
-
container_service: ContainerService,
|
|
33
|
-
logging_service: LoggingService,
|
|
34
|
-
):
|
|
35
|
-
super(ConnectService, self).__init__(
|
|
36
|
-
)
|
|
37
|
-
self.__thestage_api_client = thestage_api_client
|
|
38
|
-
self.__instance_service = instance_service
|
|
39
|
-
self.__container_service = container_service
|
|
40
|
-
self.__logging_service = logging_service
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@error_handler()
|
|
44
|
-
def connect_to_entity(
|
|
45
|
-
self,
|
|
46
|
-
uid: str,
|
|
47
|
-
username: Optional[str],
|
|
48
|
-
private_key_path: Optional[str],
|
|
49
|
-
):
|
|
50
|
-
try:
|
|
51
|
-
instance_selfhosted = self.__thestage_api_client.get_selfhosted_instance(instance_slug=uid)
|
|
52
|
-
except HttpClientException as e:
|
|
53
|
-
if e.get_status_code() == 403:
|
|
54
|
-
typer.echo("Missing permission to view self-hosted instances")
|
|
55
|
-
instance_selfhosted = None
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=uid)
|
|
59
|
-
except HttpClientException as e:
|
|
60
|
-
if e.get_status_code() == 403:
|
|
61
|
-
typer.echo("Missing permission to view rented instances")
|
|
62
|
-
instance_rented = None
|
|
63
|
-
|
|
64
|
-
try:
|
|
65
|
-
container = self.__thestage_api_client.get_container(container_slug=uid,)
|
|
66
|
-
except HttpClientException as e:
|
|
67
|
-
if e.get_status_code() == 403:
|
|
68
|
-
typer.echo("Missing permission to view containers")
|
|
69
|
-
container = None
|
|
70
|
-
|
|
71
|
-
task: Optional[TaskDto] = None
|
|
72
|
-
if uid.isdigit():
|
|
73
|
-
try:
|
|
74
|
-
task_view_response = self.__thestage_api_client.get_task(task_id=int(uid))
|
|
75
|
-
except HttpClientException as e:
|
|
76
|
-
if e.get_status_code() == 403:
|
|
77
|
-
typer.echo("Missing permission to view tasks")
|
|
78
|
-
task_view_response = None
|
|
79
|
-
if task_view_response and task_view_response.task:
|
|
80
|
-
task = task_view_response.task
|
|
81
|
-
|
|
82
|
-
rented_exists = int(instance_rented is not None and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
|
|
83
|
-
selfhosted_exists = int(instance_selfhosted is not None)
|
|
84
|
-
container_exists = int(container is not None)
|
|
85
|
-
task_exists = int(task is not None)
|
|
86
|
-
|
|
87
|
-
rented_presence = int(rented_exists and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
|
|
88
|
-
selfhosted_presence = int(selfhosted_exists and instance_selfhosted.frontend_status.status_key == SelfhostedBusinessStatus.RUNNING)
|
|
89
|
-
container_presence = int(container_exists and (container.frontend_status.status_key == DockerContainerStatus.RUNNING or container.frontend_status.status_key == DockerContainerStatus.BUSY))
|
|
90
|
-
task_presence = int(task_exists and task.frontend_status.status_key in [TaskStatus.RUNNING, TaskStatus.SCHEDULED])
|
|
91
|
-
|
|
92
|
-
if rented_exists:
|
|
93
|
-
typer.echo(__("Found a rented instance with the provided UID in status: '%rented_status%'", {"rented_status": instance_rented.frontend_status.status_translation}))
|
|
94
|
-
|
|
95
|
-
if selfhosted_exists:
|
|
96
|
-
typer.echo(__("Found a self-hosted instance with the provided UID in status: '%selfhosted_status%'", {"selfhosted_status": instance_selfhosted.frontend_status.status_translation}))
|
|
97
|
-
|
|
98
|
-
if container_exists:
|
|
99
|
-
typer.echo(__("Found a docker container with the provided UID in status: '%container_status%'", {"container_status": container.frontend_status.status_translation}))
|
|
100
|
-
|
|
101
|
-
if task_exists:
|
|
102
|
-
typer.echo(__("Found a task with the provided ID in status: '%task_status%'", {"task_status": task.frontend_status.status_translation}))
|
|
103
|
-
|
|
104
|
-
if (rented_presence + selfhosted_presence + container_presence + task_presence) > 1:
|
|
105
|
-
typer.echo("Provided identifier caused ambiguity")
|
|
106
|
-
typer.echo("Consider running a dedicated command to connect to the entity you need")
|
|
107
|
-
raise typer.Exit(code=1)
|
|
108
|
-
|
|
109
|
-
if (rented_presence + selfhosted_presence + container_presence + task_presence) == 0:
|
|
110
|
-
typer.echo("There is nothing to connect to with the provided identifier")
|
|
111
|
-
raise typer.Exit(code=1)
|
|
112
|
-
|
|
113
|
-
if rented_presence:
|
|
114
|
-
check_command_permission(CliCommand.INSTANCE_RENTED_CONNECT)
|
|
115
|
-
typer.echo("Connecting to rented instance...")
|
|
116
|
-
self.__instance_service.connect_to_rented_instance(
|
|
117
|
-
instance_rented_slug=uid,
|
|
118
|
-
input_ssh_key_path=private_key_path
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
if container_presence:
|
|
122
|
-
check_command_permission(CliCommand.CONTAINER_CONNECT)
|
|
123
|
-
typer.echo("Connecting to docker container...")
|
|
124
|
-
self.__container_service.connect_to_container(
|
|
125
|
-
container_uid=uid,
|
|
126
|
-
username=username,
|
|
127
|
-
input_ssh_key_path=private_key_path
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
if selfhosted_presence:
|
|
131
|
-
check_command_permission(CliCommand.INSTANCE_SELF_HOSTED_CONNECT)
|
|
132
|
-
typer.echo("Connecting to self-hosted instance...")
|
|
133
|
-
|
|
134
|
-
self.__instance_service.connect_to_selfhosted_instance(
|
|
135
|
-
selfhosted_instance_slug=uid,
|
|
136
|
-
username=username,
|
|
137
|
-
input_ssh_key_path=private_key_path
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
if task_presence:
|
|
141
|
-
typer.echo(__("Connecting to task..."))
|
|
142
|
-
self.__logging_service.stream_task_logs_with_controls(task_id=int(uid))
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
@error_handler()
|
|
146
|
-
def upload_ssh_key(self, public_key_contents: str, instance_slug: Optional[str]):
|
|
147
|
-
instance_rented: Optional[InstanceRentedDto] = None
|
|
148
|
-
if instance_slug:
|
|
149
|
-
try:
|
|
150
|
-
instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=instance_slug)
|
|
151
|
-
except HttpClientException as e:
|
|
152
|
-
instance_rented = None
|
|
153
|
-
|
|
154
|
-
# if no instances found - exit 1
|
|
155
|
-
if instance_rented is None:
|
|
156
|
-
typer.echo(f"No rented instance found with matching unique ID '{instance_slug}'")
|
|
157
|
-
raise typer.Exit(1)
|
|
158
|
-
|
|
159
|
-
note_to_send: Optional[str] = None
|
|
160
|
-
|
|
161
|
-
is_user_already_has_key_response = self.__thestage_api_client.is_user_has_ssh_public_key(
|
|
162
|
-
public_key=public_key_contents
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
ssh_key_pair_id = is_user_already_has_key_response.sshKeyPairId
|
|
166
|
-
is_adding_key_to_user = not is_user_already_has_key_response.isUserHasPublicKey
|
|
167
|
-
|
|
168
|
-
if is_adding_key_to_user and not note_to_send:
|
|
169
|
-
note_to_send: str = typer.prompt(
|
|
170
|
-
text=__('SSH key will be added to your profile. Please provide a title for this key'),
|
|
171
|
-
show_choices=False,
|
|
172
|
-
type=str,
|
|
173
|
-
show_default=False,
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
if not is_adding_key_to_user and not instance_rented:
|
|
177
|
-
typer.echo("Key already exists on your profile")
|
|
178
|
-
|
|
179
|
-
if is_adding_key_to_user:
|
|
180
|
-
add_ssh_key_to_user_response = self.__thestage_api_client.add_public_ssh_key_to_user(
|
|
181
|
-
public_key=public_key_contents,
|
|
182
|
-
note=note_to_send
|
|
183
|
-
)
|
|
184
|
-
typer.echo(f"Public key '{note_to_send}' added to your profile")
|
|
185
|
-
ssh_key_pair_id = add_ssh_key_to_user_response.sshKeyPairId
|
|
186
|
-
|
|
187
|
-
if instance_rented:
|
|
188
|
-
self.__thestage_api_client.add_public_ssh_key_to_instance_rented(
|
|
189
|
-
instance_rented_id=instance_rented.id,
|
|
190
|
-
ssh_key_pair_id=ssh_key_pair_id
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
if instance_rented.frontend_status.status_key != InstanceRentedBusinessStatus.ONLINE:
|
|
194
|
-
typer.echo(f"Rented instance '{instance_rented.slug}' status is '{instance_rented.frontend_status.status_translation}'. Key will be added as soon as it is back online.")
|
|
195
|
-
else:
|
|
196
|
-
typer.echo(f"Public key added to rented instance '{instance_rented.slug}'")
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import typer
|
|
3
|
+
|
|
4
|
+
from thestage.cli_command import CliCommand
|
|
5
|
+
from thestage.cli_command_helper import check_command_permission
|
|
6
|
+
from thestage.i18n.translation import __
|
|
7
|
+
from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
|
|
8
|
+
from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
|
|
9
|
+
from thestage.services.clients.thestage_api.dtos.enums.selfhosted_status import SelfhostedBusinessStatus
|
|
10
|
+
from thestage.services.clients.thestage_api.dtos.enums.instance_rented_status import InstanceRentedBusinessStatus
|
|
11
|
+
from thestage.services.abstract_service import AbstractService
|
|
12
|
+
from thestage.helpers.error_handler import error_handler
|
|
13
|
+
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
14
|
+
from thestage.services.clients.thestage_api.dtos.enums.task_status import TaskStatus
|
|
15
|
+
from thestage.services.clients.thestage_api.dtos.instance_rented_response import InstanceRentedDto
|
|
16
|
+
from thestage.services.container.container_service import ContainerService
|
|
17
|
+
from thestage.services.instance.instance_service import InstanceService
|
|
18
|
+
from thestage.services.logging.logging_service import LoggingService
|
|
19
|
+
from thestage.services.task.dto.task_dto import TaskDto
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ConnectService(AbstractService):
|
|
23
|
+
__thestage_api_client: TheStageApiClient = None
|
|
24
|
+
__instance_service: InstanceService = None
|
|
25
|
+
__container_service: ContainerService = None
|
|
26
|
+
__logging_service: LoggingService = None
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
thestage_api_client: TheStageApiClient,
|
|
31
|
+
instance_service: InstanceService,
|
|
32
|
+
container_service: ContainerService,
|
|
33
|
+
logging_service: LoggingService,
|
|
34
|
+
):
|
|
35
|
+
super(ConnectService, self).__init__(
|
|
36
|
+
)
|
|
37
|
+
self.__thestage_api_client = thestage_api_client
|
|
38
|
+
self.__instance_service = instance_service
|
|
39
|
+
self.__container_service = container_service
|
|
40
|
+
self.__logging_service = logging_service
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@error_handler()
|
|
44
|
+
def connect_to_entity(
|
|
45
|
+
self,
|
|
46
|
+
uid: str,
|
|
47
|
+
username: Optional[str],
|
|
48
|
+
private_key_path: Optional[str],
|
|
49
|
+
):
|
|
50
|
+
try:
|
|
51
|
+
instance_selfhosted = self.__thestage_api_client.get_selfhosted_instance(instance_slug=uid)
|
|
52
|
+
except HttpClientException as e:
|
|
53
|
+
if e.get_status_code() == 403:
|
|
54
|
+
typer.echo("Missing permission to view self-hosted instances")
|
|
55
|
+
instance_selfhosted = None
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=uid)
|
|
59
|
+
except HttpClientException as e:
|
|
60
|
+
if e.get_status_code() == 403:
|
|
61
|
+
typer.echo("Missing permission to view rented instances")
|
|
62
|
+
instance_rented = None
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
container = self.__thestage_api_client.get_container(container_slug=uid,)
|
|
66
|
+
except HttpClientException as e:
|
|
67
|
+
if e.get_status_code() == 403:
|
|
68
|
+
typer.echo("Missing permission to view containers")
|
|
69
|
+
container = None
|
|
70
|
+
|
|
71
|
+
task: Optional[TaskDto] = None
|
|
72
|
+
if uid.isdigit():
|
|
73
|
+
try:
|
|
74
|
+
task_view_response = self.__thestage_api_client.get_task(task_id=int(uid))
|
|
75
|
+
except HttpClientException as e:
|
|
76
|
+
if e.get_status_code() == 403:
|
|
77
|
+
typer.echo("Missing permission to view tasks")
|
|
78
|
+
task_view_response = None
|
|
79
|
+
if task_view_response and task_view_response.task:
|
|
80
|
+
task = task_view_response.task
|
|
81
|
+
|
|
82
|
+
rented_exists = int(instance_rented is not None and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
|
|
83
|
+
selfhosted_exists = int(instance_selfhosted is not None)
|
|
84
|
+
container_exists = int(container is not None)
|
|
85
|
+
task_exists = int(task is not None)
|
|
86
|
+
|
|
87
|
+
rented_presence = int(rented_exists and instance_rented.frontend_status.status_key == InstanceRentedBusinessStatus.ONLINE)
|
|
88
|
+
selfhosted_presence = int(selfhosted_exists and instance_selfhosted.frontend_status.status_key == SelfhostedBusinessStatus.RUNNING)
|
|
89
|
+
container_presence = int(container_exists and (container.frontend_status.status_key == DockerContainerStatus.RUNNING or container.frontend_status.status_key == DockerContainerStatus.BUSY))
|
|
90
|
+
task_presence = int(task_exists and task.frontend_status.status_key in [TaskStatus.RUNNING, TaskStatus.SCHEDULED])
|
|
91
|
+
|
|
92
|
+
if rented_exists:
|
|
93
|
+
typer.echo(__("Found a rented instance with the provided UID in status: '%rented_status%'", {"rented_status": instance_rented.frontend_status.status_translation}))
|
|
94
|
+
|
|
95
|
+
if selfhosted_exists:
|
|
96
|
+
typer.echo(__("Found a self-hosted instance with the provided UID in status: '%selfhosted_status%'", {"selfhosted_status": instance_selfhosted.frontend_status.status_translation}))
|
|
97
|
+
|
|
98
|
+
if container_exists:
|
|
99
|
+
typer.echo(__("Found a docker container with the provided UID in status: '%container_status%'", {"container_status": container.frontend_status.status_translation}))
|
|
100
|
+
|
|
101
|
+
if task_exists:
|
|
102
|
+
typer.echo(__("Found a task with the provided ID in status: '%task_status%'", {"task_status": task.frontend_status.status_translation}))
|
|
103
|
+
|
|
104
|
+
if (rented_presence + selfhosted_presence + container_presence + task_presence) > 1:
|
|
105
|
+
typer.echo("Provided identifier caused ambiguity")
|
|
106
|
+
typer.echo("Consider running a dedicated command to connect to the entity you need")
|
|
107
|
+
raise typer.Exit(code=1)
|
|
108
|
+
|
|
109
|
+
if (rented_presence + selfhosted_presence + container_presence + task_presence) == 0:
|
|
110
|
+
typer.echo("There is nothing to connect to with the provided identifier")
|
|
111
|
+
raise typer.Exit(code=1)
|
|
112
|
+
|
|
113
|
+
if rented_presence:
|
|
114
|
+
check_command_permission(CliCommand.INSTANCE_RENTED_CONNECT)
|
|
115
|
+
typer.echo("Connecting to rented instance...")
|
|
116
|
+
self.__instance_service.connect_to_rented_instance(
|
|
117
|
+
instance_rented_slug=uid,
|
|
118
|
+
input_ssh_key_path=private_key_path
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if container_presence:
|
|
122
|
+
check_command_permission(CliCommand.CONTAINER_CONNECT)
|
|
123
|
+
typer.echo("Connecting to docker container...")
|
|
124
|
+
self.__container_service.connect_to_container(
|
|
125
|
+
container_uid=uid,
|
|
126
|
+
username=username,
|
|
127
|
+
input_ssh_key_path=private_key_path
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if selfhosted_presence:
|
|
131
|
+
check_command_permission(CliCommand.INSTANCE_SELF_HOSTED_CONNECT)
|
|
132
|
+
typer.echo("Connecting to self-hosted instance...")
|
|
133
|
+
|
|
134
|
+
self.__instance_service.connect_to_selfhosted_instance(
|
|
135
|
+
selfhosted_instance_slug=uid,
|
|
136
|
+
username=username,
|
|
137
|
+
input_ssh_key_path=private_key_path
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if task_presence:
|
|
141
|
+
typer.echo(__("Connecting to task..."))
|
|
142
|
+
self.__logging_service.stream_task_logs_with_controls(task_id=int(uid))
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@error_handler()
|
|
146
|
+
def upload_ssh_key(self, public_key_contents: str, instance_slug: Optional[str]):
|
|
147
|
+
instance_rented: Optional[InstanceRentedDto] = None
|
|
148
|
+
if instance_slug:
|
|
149
|
+
try:
|
|
150
|
+
instance_rented = self.__thestage_api_client.get_rented_instance(instance_slug=instance_slug)
|
|
151
|
+
except HttpClientException as e:
|
|
152
|
+
instance_rented = None
|
|
153
|
+
|
|
154
|
+
# if no instances found - exit 1
|
|
155
|
+
if instance_rented is None:
|
|
156
|
+
typer.echo(f"No rented instance found with matching unique ID '{instance_slug}'")
|
|
157
|
+
raise typer.Exit(1)
|
|
158
|
+
|
|
159
|
+
note_to_send: Optional[str] = None
|
|
160
|
+
|
|
161
|
+
is_user_already_has_key_response = self.__thestage_api_client.is_user_has_ssh_public_key(
|
|
162
|
+
public_key=public_key_contents
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
ssh_key_pair_id = is_user_already_has_key_response.sshKeyPairId
|
|
166
|
+
is_adding_key_to_user = not is_user_already_has_key_response.isUserHasPublicKey
|
|
167
|
+
|
|
168
|
+
if is_adding_key_to_user and not note_to_send:
|
|
169
|
+
note_to_send: str = typer.prompt(
|
|
170
|
+
text=__('SSH key will be added to your profile. Please provide a title for this key'),
|
|
171
|
+
show_choices=False,
|
|
172
|
+
type=str,
|
|
173
|
+
show_default=False,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if not is_adding_key_to_user and not instance_rented:
|
|
177
|
+
typer.echo("Key already exists on your profile")
|
|
178
|
+
|
|
179
|
+
if is_adding_key_to_user:
|
|
180
|
+
add_ssh_key_to_user_response = self.__thestage_api_client.add_public_ssh_key_to_user(
|
|
181
|
+
public_key=public_key_contents,
|
|
182
|
+
note=note_to_send
|
|
183
|
+
)
|
|
184
|
+
typer.echo(f"Public key '{note_to_send}' added to your profile")
|
|
185
|
+
ssh_key_pair_id = add_ssh_key_to_user_response.sshKeyPairId
|
|
186
|
+
|
|
187
|
+
if instance_rented:
|
|
188
|
+
self.__thestage_api_client.add_public_ssh_key_to_instance_rented(
|
|
189
|
+
instance_rented_id=instance_rented.id,
|
|
190
|
+
ssh_key_pair_id=ssh_key_pair_id
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if instance_rented.frontend_status.status_key != InstanceRentedBusinessStatus.ONLINE:
|
|
194
|
+
typer.echo(f"Rented instance '{instance_rented.slug}' status is '{instance_rented.frontend_status.status_translation}'. Key will be added as soon as it is back online.")
|
|
195
|
+
else:
|
|
196
|
+
typer.echo(f"Public key added to rented instance '{instance_rented.slug}'")
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from typing import Optional, Dict
|
|
2
|
-
|
|
3
|
-
from pydantic import Field, ConfigDict, BaseModel
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class RemoteServerConfig(BaseModel):
|
|
7
|
-
model_config = ConfigDict(use_enum_values=True)
|
|
8
|
-
|
|
9
|
-
ip_address_to_ssh_key_map: Dict[str, str] = Field(None, alias='ip_address_to_ssh_key_map')
|
|
1
|
+
from typing import Optional, Dict
|
|
2
|
+
|
|
3
|
+
from pydantic import Field, ConfigDict, BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RemoteServerConfig(BaseModel):
|
|
7
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
8
|
+
|
|
9
|
+
ip_address_to_ssh_key_map: Dict[str, str] = Field(None, alias='ip_address_to_ssh_key_map')
|
|
File without changes
|