thestage 0.6.2__py3-none-any.whl → 0.6.3__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 +4 -5
- thestage/__init__.py +3 -3
- 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 -810
- 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 -436
- thestage/services/clients/thestage_api/__init__.py +0 -0
- thestage/services/clients/thestage_api/api_client.py +718 -718
- 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 -13
- 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 +193 -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 -1253
- 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.6.2.dist-info → thestage-0.6.3.dist-info}/LICENSE.txt +12 -12
- {thestage-0.6.2.dist-info → thestage-0.6.3.dist-info}/METADATA +3 -2
- thestage-0.6.3.dist-info/RECORD +176 -0
- {thestage-0.6.2.dist-info → thestage-0.6.3.dist-info}/WHEEL +1 -1
- thestage-0.6.2.dist-info/RECORD +0 -176
- {thestage-0.6.2.dist-info → thestage-0.6.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,389 +1,389 @@
|
|
|
1
|
-
|
|
2
|
-
import re
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Optional, List
|
|
5
|
-
|
|
6
|
-
from thestage.cli_command import CliCommand
|
|
7
|
-
from thestage.cli_command_helper import get_command_metadata, check_command_permission
|
|
8
|
-
from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
|
|
9
|
-
from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
|
|
10
|
-
from thestage.i18n.translation import __
|
|
11
|
-
from thestage.services.container.container_service import ContainerService
|
|
12
|
-
from thestage.helpers.logger.app_logger import app_logger
|
|
13
|
-
from thestage.controllers.utils_controller import validate_config_and_get_service_factory, get_current_directory
|
|
14
|
-
|
|
15
|
-
import typer
|
|
16
|
-
|
|
17
|
-
from thestage.services.logging.logging_service import LoggingService
|
|
18
|
-
|
|
19
|
-
app = typer.Typer(no_args_is_help=True, help=__("Manage containers"))
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@app.command(name='ls', help=__("List containers"), **get_command_metadata(CliCommand.CONTAINER_LS))
|
|
23
|
-
def list_containers(
|
|
24
|
-
row: int = typer.Option(
|
|
25
|
-
5,
|
|
26
|
-
'--row',
|
|
27
|
-
'-r',
|
|
28
|
-
help=__("Set number of rows displayed per page"),
|
|
29
|
-
is_eager=False,
|
|
30
|
-
),
|
|
31
|
-
page: int = typer.Option(
|
|
32
|
-
1,
|
|
33
|
-
'--page',
|
|
34
|
-
'-p',
|
|
35
|
-
help=__("Set starting page for displaying output"),
|
|
36
|
-
is_eager=False,
|
|
37
|
-
),
|
|
38
|
-
project_uid: str = typer.Option(
|
|
39
|
-
None,
|
|
40
|
-
'--project-uid',
|
|
41
|
-
'-puid',
|
|
42
|
-
help=__("Filter containers by project unique ID"),
|
|
43
|
-
is_eager=False,
|
|
44
|
-
),
|
|
45
|
-
statuses: List[str] = typer.Option(
|
|
46
|
-
None,
|
|
47
|
-
'--status',
|
|
48
|
-
'-s',
|
|
49
|
-
help=__("Filter by status, use --status all to list all containers"),
|
|
50
|
-
is_eager=False,
|
|
51
|
-
),
|
|
52
|
-
):
|
|
53
|
-
command_name = CliCommand.CONTAINER_LS
|
|
54
|
-
app_logger.info(f'Running {command_name}')
|
|
55
|
-
check_command_permission(command_name)
|
|
56
|
-
|
|
57
|
-
service_factory = validate_config_and_get_service_factory()
|
|
58
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
59
|
-
container_service.print_container_list(
|
|
60
|
-
row=row,
|
|
61
|
-
page=page,
|
|
62
|
-
project_uid=project_uid,
|
|
63
|
-
statuses=statuses,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
typer.echo(__("Containers listing complete"))
|
|
67
|
-
raise typer.Exit(0)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@app.command(name="info", no_args_is_help=True, help=__("Get container info"), **get_command_metadata(CliCommand.CONTAINER_INFO))
|
|
71
|
-
def container_info(
|
|
72
|
-
container_uid: Optional[str] = typer.Argument(hidden=False, help=__("Container unique ID")),
|
|
73
|
-
):
|
|
74
|
-
command_name = CliCommand.CONTAINER_INFO
|
|
75
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
76
|
-
check_command_permission(command_name)
|
|
77
|
-
|
|
78
|
-
if not container_uid:
|
|
79
|
-
typer.echo(__('Container unique ID is required'))
|
|
80
|
-
raise typer.Exit(1)
|
|
81
|
-
|
|
82
|
-
service_factory = validate_config_and_get_service_factory()
|
|
83
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
84
|
-
|
|
85
|
-
container: Optional[DockerContainerDto] = container_service.get_container(
|
|
86
|
-
container_slug=container_uid,
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
if not container:
|
|
90
|
-
typer.echo(__("Container not found: %container_item%", {'container_item': str(container_uid) if container_uid else ''}))
|
|
91
|
-
raise typer.Exit(1)
|
|
92
|
-
|
|
93
|
-
typer.echo(__("STATUS: %status%", {'status': str(container.frontend_status.status_translation if container and container.frontend_status else 'UNKNOWN')}))
|
|
94
|
-
typer.echo(__("UNIQUE ID: %slug%", {'slug': str(container.slug)}))
|
|
95
|
-
typer.echo(__("TITLE: %title%", {'title': str(container.title)}))
|
|
96
|
-
typer.echo(__("IMAGE: %image%", {'image': str(container.docker_image)}))
|
|
97
|
-
|
|
98
|
-
if container.instance_rented:
|
|
99
|
-
typer.echo(
|
|
100
|
-
__("RENTED SERVER INSTANCE UNIQUE ID: %instance_slug%", {'instance_slug': str(container.instance_rented.slug)})
|
|
101
|
-
)
|
|
102
|
-
typer.echo(
|
|
103
|
-
__("RENTED SERVER INSTANCE STATUS: %instance_status%",
|
|
104
|
-
{'instance_status': str(container.instance_rented.frontend_status.status_translation if container.instance_rented.frontend_status else 'UNKNOWN')})
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
if container.selfhosted_instance:
|
|
108
|
-
typer.echo(
|
|
109
|
-
__("SELF-HOSTED INSTANCE UNIQUE ID: %instance_slug%", {'instance_slug': str(container.selfhosted_instance.slug)})
|
|
110
|
-
)
|
|
111
|
-
typer.echo(
|
|
112
|
-
__("SELF-HOSTED INSTANCE STATUS: %instance_status%",
|
|
113
|
-
{'instance_status': str(container.selfhosted_instance.frontend_status.status_translation if container.selfhosted_instance.frontend_status else 'UNKNOWN')})
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
if container.mappings and (container.mappings.port_mappings or container.mappings.directory_mappings):
|
|
117
|
-
if container.mappings.port_mappings:
|
|
118
|
-
typer.echo(__("CONTAINER PORT MAPPING:"))
|
|
119
|
-
for src, dest in container.mappings.port_mappings.items():
|
|
120
|
-
typer.echo(f" {src} : {dest}")
|
|
121
|
-
|
|
122
|
-
if container.mappings.directory_mappings:
|
|
123
|
-
typer.echo(__("CONTAINER DIRECTORY MAPPING:"))
|
|
124
|
-
for src, dest in container.mappings.directory_mappings.items():
|
|
125
|
-
typer.echo(f" {src} : {dest}")
|
|
126
|
-
|
|
127
|
-
raise typer.Exit(0)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@app.command(name="connect", no_args_is_help=True, help=__("Connect to container"), **get_command_metadata(CliCommand.CONTAINER_CONNECT))
|
|
131
|
-
def container_connect(
|
|
132
|
-
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"),),
|
|
133
|
-
username: Optional[str] = typer.Option(
|
|
134
|
-
None,
|
|
135
|
-
'--username',
|
|
136
|
-
'-u',
|
|
137
|
-
help=__("Username for the server instance (required when connecting to self-hosted instance)"),
|
|
138
|
-
is_eager=False,
|
|
139
|
-
),
|
|
140
|
-
private_ssh_key_path: str = typer.Option(
|
|
141
|
-
None,
|
|
142
|
-
"--private-key-path",
|
|
143
|
-
"-pk",
|
|
144
|
-
help=__("Path to private key that will be accepted by remote server (optional)"),
|
|
145
|
-
is_eager=False,
|
|
146
|
-
),
|
|
147
|
-
):
|
|
148
|
-
command_name = CliCommand.CONTAINER_CONNECT
|
|
149
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
150
|
-
check_command_permission(command_name)
|
|
151
|
-
|
|
152
|
-
if not container_uid:
|
|
153
|
-
typer.echo(__('Container unique ID is required'))
|
|
154
|
-
raise typer.Exit(1)
|
|
155
|
-
|
|
156
|
-
if private_ssh_key_path and not Path(private_ssh_key_path).is_file():
|
|
157
|
-
typer.echo(f'No file found at provided path {private_ssh_key_path}')
|
|
158
|
-
raise typer.Exit(1)
|
|
159
|
-
|
|
160
|
-
service_factory = validate_config_and_get_service_factory()
|
|
161
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
162
|
-
|
|
163
|
-
container_service.connect_to_container(
|
|
164
|
-
container_uid=container_uid,
|
|
165
|
-
username=username,
|
|
166
|
-
input_ssh_key_path=private_ssh_key_path,
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
app_logger.info(f'Stop connect to container')
|
|
170
|
-
raise typer.Exit(0)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
@app.command(name="upload", no_args_is_help=True, help=__("Upload file to container"), **get_command_metadata(CliCommand.CONTAINER_UPLOAD))
|
|
174
|
-
def upload_file(
|
|
175
|
-
source_path: str = typer.Argument(help=__("Source file path"),),
|
|
176
|
-
destination: Optional[str] = typer.Argument(help=__("Destination directory path in container. Format: container_uid:/path/to/file"),),
|
|
177
|
-
username: Optional[str] = typer.Option(
|
|
178
|
-
None,
|
|
179
|
-
'--username',
|
|
180
|
-
'-u',
|
|
181
|
-
help=__("Username for the server instance (required when connecting to self-hosted instance)"),
|
|
182
|
-
is_eager=False,
|
|
183
|
-
),
|
|
184
|
-
):
|
|
185
|
-
command_name = CliCommand.CONTAINER_UPLOAD
|
|
186
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
187
|
-
check_command_permission(command_name)
|
|
188
|
-
|
|
189
|
-
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", destination)
|
|
190
|
-
|
|
191
|
-
if container_args is None:
|
|
192
|
-
typer.echo(__('Container unique ID and source file path are required as the second argument'))
|
|
193
|
-
typer.echo(__('Example: container_uid:/path/to/file'))
|
|
194
|
-
raise typer.Exit(1)
|
|
195
|
-
container_slug = container_args.groups()[0]
|
|
196
|
-
destination_path = container_args.groups()[1].rstrip("/")
|
|
197
|
-
|
|
198
|
-
if not container_slug:
|
|
199
|
-
typer.echo(__('Container unique ID is required'))
|
|
200
|
-
raise typer.Exit(1)
|
|
201
|
-
|
|
202
|
-
service_factory = validate_config_and_get_service_factory()
|
|
203
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
204
|
-
|
|
205
|
-
container: Optional[DockerContainerDto] = container_service.get_container(
|
|
206
|
-
container_slug=container_slug,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
if container:
|
|
210
|
-
container_service.check_if_container_running(
|
|
211
|
-
container=container
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
typer.echo(__("Uploading file(s) to container '%container-slug%'", {'container-slug': container_slug}))
|
|
215
|
-
|
|
216
|
-
container_service.put_file_to_container(
|
|
217
|
-
container=container,
|
|
218
|
-
src_path=source_path,
|
|
219
|
-
destination_path=destination_path,
|
|
220
|
-
username_param=username,
|
|
221
|
-
copy_only_folder_contents=source_path.endswith("/")
|
|
222
|
-
)
|
|
223
|
-
else:
|
|
224
|
-
typer.echo(__("Container not found: %container_item%", {'container_item': container_slug}))
|
|
225
|
-
|
|
226
|
-
app_logger.info(f'End send files to container')
|
|
227
|
-
raise typer.Exit(0)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
@app.command(name="download", no_args_is_help=True, help=__("Download file from container"), **get_command_metadata(CliCommand.CONTAINER_DOWNLOAD))
|
|
231
|
-
def download_file(
|
|
232
|
-
source_path: str = typer.Argument(help=__("Source file path in container. Format: container_uid:/path/to/file"),),
|
|
233
|
-
destination_path: str = typer.Argument(help=__("Destination directory path on local machine"),),
|
|
234
|
-
username: Optional[str] = typer.Option(
|
|
235
|
-
None,
|
|
236
|
-
'--username',
|
|
237
|
-
'-u',
|
|
238
|
-
help=__("Username for the server instance (required when connecting to self-hosted instance)"),
|
|
239
|
-
is_eager=False,
|
|
240
|
-
),
|
|
241
|
-
):
|
|
242
|
-
command_name = CliCommand.CONTAINER_DOWNLOAD
|
|
243
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
244
|
-
check_command_permission(command_name)
|
|
245
|
-
|
|
246
|
-
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", source_path)
|
|
247
|
-
|
|
248
|
-
if container_args is None:
|
|
249
|
-
typer.echo(__('Container unique ID and source directory path are required as the first argument'))
|
|
250
|
-
typer.echo(__('Example: container-uid:/path/to/file'))
|
|
251
|
-
raise typer.Exit(1)
|
|
252
|
-
container_slug = container_args.groups()[0]
|
|
253
|
-
source_path = container_args.groups()[1]
|
|
254
|
-
|
|
255
|
-
if not container_slug:
|
|
256
|
-
typer.echo(__('Container unique ID is required'))
|
|
257
|
-
raise typer.Exit(1)
|
|
258
|
-
|
|
259
|
-
service_factory = validate_config_and_get_service_factory()
|
|
260
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
261
|
-
|
|
262
|
-
container: Optional[DockerContainerDto] = container_service.get_container(
|
|
263
|
-
container_slug=container_slug,
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
if container:
|
|
267
|
-
container_service.check_if_container_running(
|
|
268
|
-
container=container
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
typer.echo(__("Downloading files from container: '%container-slug%'", {'container-slug': container_slug}))
|
|
272
|
-
|
|
273
|
-
container_service.get_file_from_container(
|
|
274
|
-
container=container,
|
|
275
|
-
src_path=source_path,
|
|
276
|
-
destination_path=destination_path.rstrip("/"),
|
|
277
|
-
username_param=username,
|
|
278
|
-
copy_only_folder_contents=source_path.endswith("/"),
|
|
279
|
-
)
|
|
280
|
-
else:
|
|
281
|
-
typer.echo(__("Container not found: %container_item%", {'container_item': container_slug}))
|
|
282
|
-
|
|
283
|
-
app_logger.info(f'End download files from container')
|
|
284
|
-
raise typer.Exit(0)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
@app.command(name="start", no_args_is_help=True, help=__("Start container"), **get_command_metadata(CliCommand.CONTAINER_START))
|
|
288
|
-
def start_container(
|
|
289
|
-
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
|
|
290
|
-
):
|
|
291
|
-
command_name = CliCommand.CONTAINER_START
|
|
292
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
293
|
-
check_command_permission(command_name)
|
|
294
|
-
|
|
295
|
-
if not container_uid:
|
|
296
|
-
typer.echo(__('Container unique ID is required'))
|
|
297
|
-
raise typer.Exit(1)
|
|
298
|
-
|
|
299
|
-
service_factory = validate_config_and_get_service_factory()
|
|
300
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
301
|
-
|
|
302
|
-
container_service.request_docker_container_action(
|
|
303
|
-
container_uid=container_uid,
|
|
304
|
-
action=DockerContainerAction.START
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
app_logger.info(f'End start container')
|
|
308
|
-
raise typer.Exit(0)
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
@app.command(name="stop", no_args_is_help=True, help=__("Stop container"), **get_command_metadata(CliCommand.CONTAINER_STOP))
|
|
312
|
-
def stop_container(
|
|
313
|
-
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
|
|
314
|
-
):
|
|
315
|
-
command_name = CliCommand.CONTAINER_STOP
|
|
316
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
317
|
-
check_command_permission(command_name)
|
|
318
|
-
|
|
319
|
-
if not container_uid:
|
|
320
|
-
typer.echo(__('Container unique ID is required'))
|
|
321
|
-
raise typer.Exit(1)
|
|
322
|
-
|
|
323
|
-
service_factory = validate_config_and_get_service_factory()
|
|
324
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
325
|
-
|
|
326
|
-
container_service.request_docker_container_action(
|
|
327
|
-
container_uid=container_uid,
|
|
328
|
-
action=DockerContainerAction.STOP
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
app_logger.info(f'End stop container')
|
|
332
|
-
raise typer.Exit(0)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
@app.command(name="restart", no_args_is_help=True, help=__("Restart container"), **get_command_metadata(CliCommand.CONTAINER_RESTART))
|
|
336
|
-
def restart_container(
|
|
337
|
-
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
|
|
338
|
-
):
|
|
339
|
-
command_name = CliCommand.CONTAINER_RESTART
|
|
340
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
341
|
-
check_command_permission(command_name)
|
|
342
|
-
|
|
343
|
-
if not container_uid:
|
|
344
|
-
typer.echo(__('Container unique ID is required'))
|
|
345
|
-
raise typer.Exit(1)
|
|
346
|
-
|
|
347
|
-
service_factory = validate_config_and_get_service_factory()
|
|
348
|
-
container_service: ContainerService = service_factory.get_container_service()
|
|
349
|
-
|
|
350
|
-
container_service.request_docker_container_action(
|
|
351
|
-
container_uid=container_uid,
|
|
352
|
-
action=DockerContainerAction.RESTART
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
app_logger.info(f'End stop container')
|
|
356
|
-
raise typer.Exit(0)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
@app.command(name="logs", no_args_is_help=True, help=__("Stream real-time Docker container logs or view last logs for a container"), **get_command_metadata(CliCommand.CONTAINER_LOGS))
|
|
360
|
-
def container_logs(
|
|
361
|
-
container_uid: Optional[str] = typer.Argument(help=__("Container unique id")),
|
|
362
|
-
logs_number: Optional[int] = typer.Option(
|
|
363
|
-
None,
|
|
364
|
-
'--number',
|
|
365
|
-
'-n',
|
|
366
|
-
help=__("Display a number of latest log entries. No real-time stream if provided."),
|
|
367
|
-
is_eager=False,
|
|
368
|
-
),
|
|
369
|
-
):
|
|
370
|
-
command_name = CliCommand.CONTAINER_LOGS
|
|
371
|
-
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
372
|
-
check_command_permission(command_name)
|
|
373
|
-
|
|
374
|
-
if not container_uid:
|
|
375
|
-
typer.echo(__('Container unique ID is required'))
|
|
376
|
-
raise typer.Exit(1)
|
|
377
|
-
|
|
378
|
-
service_factory = validate_config_and_get_service_factory()
|
|
379
|
-
logging_service: LoggingService = service_factory.get_logging_service()
|
|
380
|
-
|
|
381
|
-
if logs_number is None:
|
|
382
|
-
logging_service.stream_container_logs_with_controls(
|
|
383
|
-
container_uid=container_uid
|
|
384
|
-
)
|
|
385
|
-
else:
|
|
386
|
-
logging_service.print_last_container_logs(container_uid=container_uid, logs_number=logs_number)
|
|
387
|
-
|
|
388
|
-
app_logger.info(f'Container logs - end')
|
|
389
|
-
raise typer.Exit(0)
|
|
1
|
+
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, List
|
|
5
|
+
|
|
6
|
+
from thestage.cli_command import CliCommand
|
|
7
|
+
from thestage.cli_command_helper import get_command_metadata, check_command_permission
|
|
8
|
+
from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
|
|
9
|
+
from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
|
|
10
|
+
from thestage.i18n.translation import __
|
|
11
|
+
from thestage.services.container.container_service import ContainerService
|
|
12
|
+
from thestage.helpers.logger.app_logger import app_logger
|
|
13
|
+
from thestage.controllers.utils_controller import validate_config_and_get_service_factory, get_current_directory
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
from thestage.services.logging.logging_service import LoggingService
|
|
18
|
+
|
|
19
|
+
app = typer.Typer(no_args_is_help=True, help=__("Manage containers"))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.command(name='ls', help=__("List containers"), **get_command_metadata(CliCommand.CONTAINER_LS))
|
|
23
|
+
def list_containers(
|
|
24
|
+
row: int = typer.Option(
|
|
25
|
+
5,
|
|
26
|
+
'--row',
|
|
27
|
+
'-r',
|
|
28
|
+
help=__("Set number of rows displayed per page"),
|
|
29
|
+
is_eager=False,
|
|
30
|
+
),
|
|
31
|
+
page: int = typer.Option(
|
|
32
|
+
1,
|
|
33
|
+
'--page',
|
|
34
|
+
'-p',
|
|
35
|
+
help=__("Set starting page for displaying output"),
|
|
36
|
+
is_eager=False,
|
|
37
|
+
),
|
|
38
|
+
project_uid: str = typer.Option(
|
|
39
|
+
None,
|
|
40
|
+
'--project-uid',
|
|
41
|
+
'-puid',
|
|
42
|
+
help=__("Filter containers by project unique ID"),
|
|
43
|
+
is_eager=False,
|
|
44
|
+
),
|
|
45
|
+
statuses: List[str] = typer.Option(
|
|
46
|
+
None,
|
|
47
|
+
'--status',
|
|
48
|
+
'-s',
|
|
49
|
+
help=__("Filter by status, use --status all to list all containers"),
|
|
50
|
+
is_eager=False,
|
|
51
|
+
),
|
|
52
|
+
):
|
|
53
|
+
command_name = CliCommand.CONTAINER_LS
|
|
54
|
+
app_logger.info(f'Running {command_name}')
|
|
55
|
+
check_command_permission(command_name)
|
|
56
|
+
|
|
57
|
+
service_factory = validate_config_and_get_service_factory()
|
|
58
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
59
|
+
container_service.print_container_list(
|
|
60
|
+
row=row,
|
|
61
|
+
page=page,
|
|
62
|
+
project_uid=project_uid,
|
|
63
|
+
statuses=statuses,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
typer.echo(__("Containers listing complete"))
|
|
67
|
+
raise typer.Exit(0)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@app.command(name="info", no_args_is_help=True, help=__("Get container info"), **get_command_metadata(CliCommand.CONTAINER_INFO))
|
|
71
|
+
def container_info(
|
|
72
|
+
container_uid: Optional[str] = typer.Argument(hidden=False, help=__("Container unique ID")),
|
|
73
|
+
):
|
|
74
|
+
command_name = CliCommand.CONTAINER_INFO
|
|
75
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
76
|
+
check_command_permission(command_name)
|
|
77
|
+
|
|
78
|
+
if not container_uid:
|
|
79
|
+
typer.echo(__('Container unique ID is required'))
|
|
80
|
+
raise typer.Exit(1)
|
|
81
|
+
|
|
82
|
+
service_factory = validate_config_and_get_service_factory()
|
|
83
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
84
|
+
|
|
85
|
+
container: Optional[DockerContainerDto] = container_service.get_container(
|
|
86
|
+
container_slug=container_uid,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if not container:
|
|
90
|
+
typer.echo(__("Container not found: %container_item%", {'container_item': str(container_uid) if container_uid else ''}))
|
|
91
|
+
raise typer.Exit(1)
|
|
92
|
+
|
|
93
|
+
typer.echo(__("STATUS: %status%", {'status': str(container.frontend_status.status_translation if container and container.frontend_status else 'UNKNOWN')}))
|
|
94
|
+
typer.echo(__("UNIQUE ID: %slug%", {'slug': str(container.slug)}))
|
|
95
|
+
typer.echo(__("TITLE: %title%", {'title': str(container.title)}))
|
|
96
|
+
typer.echo(__("IMAGE: %image%", {'image': str(container.docker_image)}))
|
|
97
|
+
|
|
98
|
+
if container.instance_rented:
|
|
99
|
+
typer.echo(
|
|
100
|
+
__("RENTED SERVER INSTANCE UNIQUE ID: %instance_slug%", {'instance_slug': str(container.instance_rented.slug)})
|
|
101
|
+
)
|
|
102
|
+
typer.echo(
|
|
103
|
+
__("RENTED SERVER INSTANCE STATUS: %instance_status%",
|
|
104
|
+
{'instance_status': str(container.instance_rented.frontend_status.status_translation if container.instance_rented.frontend_status else 'UNKNOWN')})
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if container.selfhosted_instance:
|
|
108
|
+
typer.echo(
|
|
109
|
+
__("SELF-HOSTED INSTANCE UNIQUE ID: %instance_slug%", {'instance_slug': str(container.selfhosted_instance.slug)})
|
|
110
|
+
)
|
|
111
|
+
typer.echo(
|
|
112
|
+
__("SELF-HOSTED INSTANCE STATUS: %instance_status%",
|
|
113
|
+
{'instance_status': str(container.selfhosted_instance.frontend_status.status_translation if container.selfhosted_instance.frontend_status else 'UNKNOWN')})
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if container.mappings and (container.mappings.port_mappings or container.mappings.directory_mappings):
|
|
117
|
+
if container.mappings.port_mappings:
|
|
118
|
+
typer.echo(__("CONTAINER PORT MAPPING:"))
|
|
119
|
+
for src, dest in container.mappings.port_mappings.items():
|
|
120
|
+
typer.echo(f" {src} : {dest}")
|
|
121
|
+
|
|
122
|
+
if container.mappings.directory_mappings:
|
|
123
|
+
typer.echo(__("CONTAINER DIRECTORY MAPPING:"))
|
|
124
|
+
for src, dest in container.mappings.directory_mappings.items():
|
|
125
|
+
typer.echo(f" {src} : {dest}")
|
|
126
|
+
|
|
127
|
+
raise typer.Exit(0)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@app.command(name="connect", no_args_is_help=True, help=__("Connect to container"), **get_command_metadata(CliCommand.CONTAINER_CONNECT))
|
|
131
|
+
def container_connect(
|
|
132
|
+
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"),),
|
|
133
|
+
username: Optional[str] = typer.Option(
|
|
134
|
+
None,
|
|
135
|
+
'--username',
|
|
136
|
+
'-u',
|
|
137
|
+
help=__("Username for the server instance (required when connecting to self-hosted instance)"),
|
|
138
|
+
is_eager=False,
|
|
139
|
+
),
|
|
140
|
+
private_ssh_key_path: str = typer.Option(
|
|
141
|
+
None,
|
|
142
|
+
"--private-key-path",
|
|
143
|
+
"-pk",
|
|
144
|
+
help=__("Path to private key that will be accepted by remote server (optional)"),
|
|
145
|
+
is_eager=False,
|
|
146
|
+
),
|
|
147
|
+
):
|
|
148
|
+
command_name = CliCommand.CONTAINER_CONNECT
|
|
149
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
150
|
+
check_command_permission(command_name)
|
|
151
|
+
|
|
152
|
+
if not container_uid:
|
|
153
|
+
typer.echo(__('Container unique ID is required'))
|
|
154
|
+
raise typer.Exit(1)
|
|
155
|
+
|
|
156
|
+
if private_ssh_key_path and not Path(private_ssh_key_path).is_file():
|
|
157
|
+
typer.echo(f'No file found at provided path {private_ssh_key_path}')
|
|
158
|
+
raise typer.Exit(1)
|
|
159
|
+
|
|
160
|
+
service_factory = validate_config_and_get_service_factory()
|
|
161
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
162
|
+
|
|
163
|
+
container_service.connect_to_container(
|
|
164
|
+
container_uid=container_uid,
|
|
165
|
+
username=username,
|
|
166
|
+
input_ssh_key_path=private_ssh_key_path,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
app_logger.info(f'Stop connect to container')
|
|
170
|
+
raise typer.Exit(0)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@app.command(name="upload", no_args_is_help=True, help=__("Upload file to container"), **get_command_metadata(CliCommand.CONTAINER_UPLOAD))
|
|
174
|
+
def upload_file(
|
|
175
|
+
source_path: str = typer.Argument(help=__("Source file path"),),
|
|
176
|
+
destination: Optional[str] = typer.Argument(help=__("Destination directory path in container. Format: container_uid:/path/to/file"),),
|
|
177
|
+
username: Optional[str] = typer.Option(
|
|
178
|
+
None,
|
|
179
|
+
'--username',
|
|
180
|
+
'-u',
|
|
181
|
+
help=__("Username for the server instance (required when connecting to self-hosted instance)"),
|
|
182
|
+
is_eager=False,
|
|
183
|
+
),
|
|
184
|
+
):
|
|
185
|
+
command_name = CliCommand.CONTAINER_UPLOAD
|
|
186
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
187
|
+
check_command_permission(command_name)
|
|
188
|
+
|
|
189
|
+
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", destination)
|
|
190
|
+
|
|
191
|
+
if container_args is None:
|
|
192
|
+
typer.echo(__('Container unique ID and source file path are required as the second argument'))
|
|
193
|
+
typer.echo(__('Example: container_uid:/path/to/file'))
|
|
194
|
+
raise typer.Exit(1)
|
|
195
|
+
container_slug = container_args.groups()[0]
|
|
196
|
+
destination_path = container_args.groups()[1].rstrip("/")
|
|
197
|
+
|
|
198
|
+
if not container_slug:
|
|
199
|
+
typer.echo(__('Container unique ID is required'))
|
|
200
|
+
raise typer.Exit(1)
|
|
201
|
+
|
|
202
|
+
service_factory = validate_config_and_get_service_factory()
|
|
203
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
204
|
+
|
|
205
|
+
container: Optional[DockerContainerDto] = container_service.get_container(
|
|
206
|
+
container_slug=container_slug,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
if container:
|
|
210
|
+
container_service.check_if_container_running(
|
|
211
|
+
container=container
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
typer.echo(__("Uploading file(s) to container '%container-slug%'", {'container-slug': container_slug}))
|
|
215
|
+
|
|
216
|
+
container_service.put_file_to_container(
|
|
217
|
+
container=container,
|
|
218
|
+
src_path=source_path,
|
|
219
|
+
destination_path=destination_path,
|
|
220
|
+
username_param=username,
|
|
221
|
+
copy_only_folder_contents=source_path.endswith("/")
|
|
222
|
+
)
|
|
223
|
+
else:
|
|
224
|
+
typer.echo(__("Container not found: %container_item%", {'container_item': container_slug}))
|
|
225
|
+
|
|
226
|
+
app_logger.info(f'End send files to container')
|
|
227
|
+
raise typer.Exit(0)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@app.command(name="download", no_args_is_help=True, help=__("Download file from container"), **get_command_metadata(CliCommand.CONTAINER_DOWNLOAD))
|
|
231
|
+
def download_file(
|
|
232
|
+
source_path: str = typer.Argument(help=__("Source file path in container. Format: container_uid:/path/to/file"),),
|
|
233
|
+
destination_path: str = typer.Argument(help=__("Destination directory path on local machine"),),
|
|
234
|
+
username: Optional[str] = typer.Option(
|
|
235
|
+
None,
|
|
236
|
+
'--username',
|
|
237
|
+
'-u',
|
|
238
|
+
help=__("Username for the server instance (required when connecting to self-hosted instance)"),
|
|
239
|
+
is_eager=False,
|
|
240
|
+
),
|
|
241
|
+
):
|
|
242
|
+
command_name = CliCommand.CONTAINER_DOWNLOAD
|
|
243
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
244
|
+
check_command_permission(command_name)
|
|
245
|
+
|
|
246
|
+
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", source_path)
|
|
247
|
+
|
|
248
|
+
if container_args is None:
|
|
249
|
+
typer.echo(__('Container unique ID and source directory path are required as the first argument'))
|
|
250
|
+
typer.echo(__('Example: container-uid:/path/to/file'))
|
|
251
|
+
raise typer.Exit(1)
|
|
252
|
+
container_slug = container_args.groups()[0]
|
|
253
|
+
source_path = container_args.groups()[1]
|
|
254
|
+
|
|
255
|
+
if not container_slug:
|
|
256
|
+
typer.echo(__('Container unique ID is required'))
|
|
257
|
+
raise typer.Exit(1)
|
|
258
|
+
|
|
259
|
+
service_factory = validate_config_and_get_service_factory()
|
|
260
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
261
|
+
|
|
262
|
+
container: Optional[DockerContainerDto] = container_service.get_container(
|
|
263
|
+
container_slug=container_slug,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if container:
|
|
267
|
+
container_service.check_if_container_running(
|
|
268
|
+
container=container
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
typer.echo(__("Downloading files from container: '%container-slug%'", {'container-slug': container_slug}))
|
|
272
|
+
|
|
273
|
+
container_service.get_file_from_container(
|
|
274
|
+
container=container,
|
|
275
|
+
src_path=source_path,
|
|
276
|
+
destination_path=destination_path.rstrip("/"),
|
|
277
|
+
username_param=username,
|
|
278
|
+
copy_only_folder_contents=source_path.endswith("/"),
|
|
279
|
+
)
|
|
280
|
+
else:
|
|
281
|
+
typer.echo(__("Container not found: %container_item%", {'container_item': container_slug}))
|
|
282
|
+
|
|
283
|
+
app_logger.info(f'End download files from container')
|
|
284
|
+
raise typer.Exit(0)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@app.command(name="start", no_args_is_help=True, help=__("Start container"), **get_command_metadata(CliCommand.CONTAINER_START))
|
|
288
|
+
def start_container(
|
|
289
|
+
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
|
|
290
|
+
):
|
|
291
|
+
command_name = CliCommand.CONTAINER_START
|
|
292
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
293
|
+
check_command_permission(command_name)
|
|
294
|
+
|
|
295
|
+
if not container_uid:
|
|
296
|
+
typer.echo(__('Container unique ID is required'))
|
|
297
|
+
raise typer.Exit(1)
|
|
298
|
+
|
|
299
|
+
service_factory = validate_config_and_get_service_factory()
|
|
300
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
301
|
+
|
|
302
|
+
container_service.request_docker_container_action(
|
|
303
|
+
container_uid=container_uid,
|
|
304
|
+
action=DockerContainerAction.START
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
app_logger.info(f'End start container')
|
|
308
|
+
raise typer.Exit(0)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@app.command(name="stop", no_args_is_help=True, help=__("Stop container"), **get_command_metadata(CliCommand.CONTAINER_STOP))
|
|
312
|
+
def stop_container(
|
|
313
|
+
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
|
|
314
|
+
):
|
|
315
|
+
command_name = CliCommand.CONTAINER_STOP
|
|
316
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
317
|
+
check_command_permission(command_name)
|
|
318
|
+
|
|
319
|
+
if not container_uid:
|
|
320
|
+
typer.echo(__('Container unique ID is required'))
|
|
321
|
+
raise typer.Exit(1)
|
|
322
|
+
|
|
323
|
+
service_factory = validate_config_and_get_service_factory()
|
|
324
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
325
|
+
|
|
326
|
+
container_service.request_docker_container_action(
|
|
327
|
+
container_uid=container_uid,
|
|
328
|
+
action=DockerContainerAction.STOP
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
app_logger.info(f'End stop container')
|
|
332
|
+
raise typer.Exit(0)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@app.command(name="restart", no_args_is_help=True, help=__("Restart container"), **get_command_metadata(CliCommand.CONTAINER_RESTART))
|
|
336
|
+
def restart_container(
|
|
337
|
+
container_uid: Optional[str] = typer.Argument(help=__("Container unique ID"), ),
|
|
338
|
+
):
|
|
339
|
+
command_name = CliCommand.CONTAINER_RESTART
|
|
340
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
341
|
+
check_command_permission(command_name)
|
|
342
|
+
|
|
343
|
+
if not container_uid:
|
|
344
|
+
typer.echo(__('Container unique ID is required'))
|
|
345
|
+
raise typer.Exit(1)
|
|
346
|
+
|
|
347
|
+
service_factory = validate_config_and_get_service_factory()
|
|
348
|
+
container_service: ContainerService = service_factory.get_container_service()
|
|
349
|
+
|
|
350
|
+
container_service.request_docker_container_action(
|
|
351
|
+
container_uid=container_uid,
|
|
352
|
+
action=DockerContainerAction.RESTART
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
app_logger.info(f'End stop container')
|
|
356
|
+
raise typer.Exit(0)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@app.command(name="logs", no_args_is_help=True, help=__("Stream real-time Docker container logs or view last logs for a container"), **get_command_metadata(CliCommand.CONTAINER_LOGS))
|
|
360
|
+
def container_logs(
|
|
361
|
+
container_uid: Optional[str] = typer.Argument(help=__("Container unique id")),
|
|
362
|
+
logs_number: Optional[int] = typer.Option(
|
|
363
|
+
None,
|
|
364
|
+
'--number',
|
|
365
|
+
'-n',
|
|
366
|
+
help=__("Display a number of latest log entries. No real-time stream if provided."),
|
|
367
|
+
is_eager=False,
|
|
368
|
+
),
|
|
369
|
+
):
|
|
370
|
+
command_name = CliCommand.CONTAINER_LOGS
|
|
371
|
+
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
372
|
+
check_command_permission(command_name)
|
|
373
|
+
|
|
374
|
+
if not container_uid:
|
|
375
|
+
typer.echo(__('Container unique ID is required'))
|
|
376
|
+
raise typer.Exit(1)
|
|
377
|
+
|
|
378
|
+
service_factory = validate_config_and_get_service_factory()
|
|
379
|
+
logging_service: LoggingService = service_factory.get_logging_service()
|
|
380
|
+
|
|
381
|
+
if logs_number is None:
|
|
382
|
+
logging_service.stream_container_logs_with_controls(
|
|
383
|
+
container_uid=container_uid
|
|
384
|
+
)
|
|
385
|
+
else:
|
|
386
|
+
logging_service.print_last_container_logs(container_uid=container_uid, logs_number=logs_number)
|
|
387
|
+
|
|
388
|
+
app_logger.info(f'Container logs - end')
|
|
389
|
+
raise typer.Exit(0)
|