thestage 0.6.6__tar.gz → 0.6.7__tar.gz
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-0.6.6 → thestage-0.6.7}/PKG-INFO +2 -1
- {thestage-0.6.6 → thestage-0.6.7}/pyproject.toml +2 -1
- {thestage-0.6.6 → thestage-0.6.7}/thestage/__init__.py +1 -1
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/container_controller.py +13 -66
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/instance_controller.py +4 -4
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/project_controller.py +8 -10
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/project_inference_simulator_model.py +1 -1
- {thestage-0.6.6 → thestage-0.6.7}/thestage/helpers/error_handler.py +2 -2
- {thestage-0.6.6 → thestage-0.6.7}/thestage/main.py +1 -1
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/git/git_client.py +1 -1
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/api_client.py +3 -5
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/base_controller/connect_resolve_response.py +1 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +0 -2
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +2 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +1 -1
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_response.py +3 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/connect/connect_service.py +6 -6
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/container/container_service.py +97 -22
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/logging/logging_service.py +5 -8
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/dto/project_config.py +1 -2
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/project_service.py +16 -12
- {thestage-0.6.6 → thestage-0.6.7}/LICENSE.txt +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/README.md +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/.env +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/__main__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/cli_command.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/cli_command_helper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/color_scheme/color_scheme.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/config/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/config/config_storage.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/config/env_base.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/base_controller.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/config_controller.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/controllers/utils_controller.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/debug_main.dist.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/container.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/enums/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/enums/order_direction_type.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/enums/shell_type.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/enums/tail_output_type.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/enums/yes_no_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/file_item.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/project_inference_simulator.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/project_task.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/rented_instance.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/entities/self_hosted_instance.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/auth_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/base_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/business_logic_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/config_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/file_system_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/git_access_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/exceptions/remote_server_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/git/ProgressPrinter.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/helpers/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/helpers/exception_hook.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/helpers/logger/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/helpers/logger/app_logger.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/helpers/ssh_util.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/i18n/en_GB/messages.po +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/i18n/translation.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/.env +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/abstract_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/abstract_service.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/app_config_service.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/git/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/core/api_client_core.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/core/http_client_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/base_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/container_param_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/container_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/docker_container_mapping.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/entity_filter_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/container_pending_action.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/container_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/cpu_type.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/gpu_name.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/inference_model_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/inference_simulator_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/instance_rented_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/provider_name.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/selfhosted_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/task_execution_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/enums/task_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/frontend_status.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/installed_service.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/instance_detected_gpus.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/instance_rented_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/logging_controller/docker_container_log_stream_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/logging_controller/task_log_stream_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/paginated_entity_list.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/pagination_data.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/project_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/sftp_path_helper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/user_controller/user_profile.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/clients/thestage_api/dtos/validate_token_response.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/config_provider/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/config_provider/config_provider.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/connect/dto/remote_server_config.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/container/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/container/mapper/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/container/mapper/container_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/core_files/config_entity.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/filesystem_service.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/instance/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/instance/instance_service.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/instance/mapper/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/instance/mapper/instance_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/instance/mapper/selfhosted_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/logging/byte_print_style.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/logging/dto/log_message.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/logging/dto/log_type.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/logging/exception/log_polling_exception.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/logging/logging_constants.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/dto/inference_simulator_dto.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/dto/inference_simulator_model_dto.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/mapper/__init__.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/mapper/project_inference_simulator_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/mapper/project_inference_simulator_model_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/project/mapper/project_task_mapper.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/remote_server_service.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/service_factory.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/task/dto/task_dto.py +0 -0
- {thestage-0.6.6 → thestage-0.6.7}/thestage/services/validation_service.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: thestage
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.7
|
|
4
4
|
Summary:
|
|
5
5
|
License-File: LICENSE.txt
|
|
6
6
|
Author: TheStage AI team
|
|
@@ -21,6 +21,7 @@ Requires-Dist: pydantic (>=2.4.2,<3.0.0)
|
|
|
21
21
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
22
22
|
Requires-Dist: python-gettext-translations (>=1.1.0,<2.0.0)
|
|
23
23
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
24
|
+
Requires-Dist: rich (>=14.2.0,<15.0.0)
|
|
24
25
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
25
26
|
Requires-Dist: typer[all] (>=0.15.2,<0.16.0)
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "thestage"
|
|
3
3
|
|
|
4
|
-
version = "0.6.
|
|
4
|
+
version = "0.6.7"
|
|
5
5
|
|
|
6
6
|
description = ""
|
|
7
7
|
authors = ["TheStage AI team <hello@thestage.ai>"]
|
|
@@ -34,6 +34,7 @@ paramiko = "^3.5.1"
|
|
|
34
34
|
aioconsole = "^0.8.0"
|
|
35
35
|
httpx = "^0.27.2"
|
|
36
36
|
boto3 = "^1.35.80"
|
|
37
|
+
rich = "^14.2.0"
|
|
37
38
|
|
|
38
39
|
[tool.poetry.dev-dependencies]
|
|
39
40
|
pytest = "^7.4.3"
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import re
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Optional, List
|
|
5
|
+
from rich import print
|
|
5
6
|
|
|
6
7
|
from thestage.cli_command import CliCommand
|
|
7
8
|
from thestage.cli_command_helper import get_command_metadata, check_command_permission
|
|
9
|
+
from thestage.color_scheme.color_scheme import ColorScheme
|
|
10
|
+
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
8
11
|
from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
|
|
9
12
|
from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
|
|
10
13
|
from thestage.i18n.translation import __
|
|
@@ -215,7 +218,7 @@ def container_connect(
|
|
|
215
218
|
@app.command(name="upload", no_args_is_help=True, help=__("Upload file to container"), **get_command_metadata(CliCommand.CONTAINER_UPLOAD))
|
|
216
219
|
def upload_file(
|
|
217
220
|
source_path: str = typer.Argument(help=__("Source file path"),),
|
|
218
|
-
destination: Optional[str] = typer.Argument(help=__("Destination directory path in container. Format:
|
|
221
|
+
destination: Optional[str] = typer.Argument(help=__("Destination directory path in container. Format: container_id_or_name:/path/to/file"),),
|
|
219
222
|
username: Optional[str] = typer.Option(
|
|
220
223
|
None,
|
|
221
224
|
'--username',
|
|
@@ -228,50 +231,22 @@ def upload_file(
|
|
|
228
231
|
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
229
232
|
check_command_permission(command_name)
|
|
230
233
|
|
|
231
|
-
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", destination)
|
|
232
|
-
|
|
233
|
-
if container_args is None:
|
|
234
|
-
typer.echo(__('Container name and source file path are required as the second argument'))
|
|
235
|
-
typer.echo(__('Example: container_uid:/path/to/file'))
|
|
236
|
-
raise typer.Exit(1)
|
|
237
|
-
container_slug = container_args.groups()[0]
|
|
238
|
-
destination_path = container_args.groups()[1].rstrip("/")
|
|
239
|
-
|
|
240
|
-
if not container_slug:
|
|
241
|
-
typer.echo(__('Container name is required'))
|
|
242
|
-
raise typer.Exit(1)
|
|
243
|
-
|
|
244
234
|
service_factory = validate_config_and_get_service_factory()
|
|
245
235
|
container_service: ContainerService = service_factory.get_container_service()
|
|
246
236
|
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
container_service.put_file_to_container(
|
|
238
|
+
source_path=source_path,
|
|
239
|
+
destination=destination,
|
|
240
|
+
username_param=username,
|
|
249
241
|
)
|
|
250
242
|
|
|
251
|
-
if container:
|
|
252
|
-
container_service.check_if_container_running(
|
|
253
|
-
container=container
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
typer.echo(f"Uploading file(s) to container '{container_slug}'")
|
|
257
|
-
|
|
258
|
-
container_service.put_file_to_container(
|
|
259
|
-
container=container,
|
|
260
|
-
src_path=source_path,
|
|
261
|
-
destination_path=destination_path,
|
|
262
|
-
username_param=username,
|
|
263
|
-
copy_only_folder_contents=source_path.endswith("/")
|
|
264
|
-
)
|
|
265
|
-
else:
|
|
266
|
-
typer.echo(__("Container not found: %container_item%", {'container_item': container_slug}))
|
|
267
|
-
|
|
268
243
|
app_logger.info(f'End send files to container')
|
|
269
244
|
raise typer.Exit(0)
|
|
270
245
|
|
|
271
246
|
|
|
272
247
|
@app.command(name="download", no_args_is_help=True, help=__("Download file from container"), **get_command_metadata(CliCommand.CONTAINER_DOWNLOAD))
|
|
273
248
|
def download_file(
|
|
274
|
-
|
|
249
|
+
source: str = typer.Argument(help=__("Source file path in container. Format: container_name:/path/to/file"),),
|
|
275
250
|
destination_path: str = typer.Argument(help=__("Destination directory path on local machine"),),
|
|
276
251
|
username: Optional[str] = typer.Option(
|
|
277
252
|
None,
|
|
@@ -285,43 +260,15 @@ def download_file(
|
|
|
285
260
|
app_logger.info(f'Running {command_name} from {get_current_directory()}')
|
|
286
261
|
check_command_permission(command_name)
|
|
287
262
|
|
|
288
|
-
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", source_path)
|
|
289
|
-
|
|
290
|
-
if container_args is None:
|
|
291
|
-
typer.echo(__('Container name and source directory path are required as the first argument'))
|
|
292
|
-
typer.echo(__('Example: container-uid:/path/to/file'))
|
|
293
|
-
raise typer.Exit(1)
|
|
294
|
-
container_slug = container_args.groups()[0]
|
|
295
|
-
source_path = container_args.groups()[1]
|
|
296
|
-
|
|
297
|
-
if not container_slug:
|
|
298
|
-
typer.echo(__('Container name is required'))
|
|
299
|
-
raise typer.Exit(1)
|
|
300
|
-
|
|
301
263
|
service_factory = validate_config_and_get_service_factory()
|
|
302
264
|
container_service: ContainerService = service_factory.get_container_service()
|
|
303
265
|
|
|
304
|
-
|
|
305
|
-
|
|
266
|
+
container_service.get_file_from_container(
|
|
267
|
+
source=source,
|
|
268
|
+
destination_path=destination_path.rstrip("/"),
|
|
269
|
+
username_param=username,
|
|
306
270
|
)
|
|
307
271
|
|
|
308
|
-
if container:
|
|
309
|
-
container_service.check_if_container_running(
|
|
310
|
-
container=container
|
|
311
|
-
)
|
|
312
|
-
|
|
313
|
-
typer.echo(f"Downloading files from container: '{container_slug}'")
|
|
314
|
-
|
|
315
|
-
container_service.get_file_from_container(
|
|
316
|
-
container=container,
|
|
317
|
-
src_path=source_path,
|
|
318
|
-
destination_path=destination_path.rstrip("/"),
|
|
319
|
-
username_param=username,
|
|
320
|
-
copy_only_folder_contents=source_path.endswith("/"),
|
|
321
|
-
)
|
|
322
|
-
else:
|
|
323
|
-
typer.echo(f"Container not found: {container_slug}")
|
|
324
|
-
|
|
325
272
|
app_logger.info(f'End download files from container')
|
|
326
273
|
raise typer.Exit(0)
|
|
327
274
|
|
|
@@ -66,14 +66,14 @@ def rented_list(
|
|
|
66
66
|
def instance_connect(
|
|
67
67
|
public_id: Optional[str] = typer.Option(
|
|
68
68
|
None,
|
|
69
|
-
'--id',
|
|
69
|
+
'--rented-instance-id',
|
|
70
70
|
'-rid',
|
|
71
71
|
help="Rented instance ID",
|
|
72
72
|
is_eager=False,
|
|
73
73
|
),
|
|
74
74
|
slug: Optional[str] = typer.Option(
|
|
75
75
|
None,
|
|
76
|
-
'--name',
|
|
76
|
+
'--rented-instance-name',
|
|
77
77
|
'-rn',
|
|
78
78
|
help="Rented instance name",
|
|
79
79
|
is_eager=False,
|
|
@@ -156,14 +156,14 @@ def self_hosted_list(
|
|
|
156
156
|
def self_hosted_connect(
|
|
157
157
|
public_id: Optional[str] = typer.Option(
|
|
158
158
|
None,
|
|
159
|
-
'--id',
|
|
159
|
+
'--self-hosted-instance-id',
|
|
160
160
|
'-sid',
|
|
161
161
|
help="Self-hosted instance ID",
|
|
162
162
|
is_eager=False,
|
|
163
163
|
),
|
|
164
164
|
slug: Optional[str] = typer.Option(
|
|
165
165
|
None,
|
|
166
|
-
'--name',
|
|
166
|
+
'--self-hosted-instance-name',
|
|
167
167
|
'-sn',
|
|
168
168
|
help="Self-hosted instance name",
|
|
169
169
|
is_eager=False,
|
|
@@ -63,7 +63,8 @@ def clone(
|
|
|
63
63
|
raise typer.Exit(1)
|
|
64
64
|
|
|
65
65
|
if not working_directory:
|
|
66
|
-
|
|
66
|
+
project_dir_name = project_public_id if project_slug is None else project_slug
|
|
67
|
+
working_directory = get_current_directory().joinpath(project_dir_name)
|
|
67
68
|
|
|
68
69
|
service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
|
|
69
70
|
project_service = service_factory.get_project_service()
|
|
@@ -842,7 +843,7 @@ def inference_simulator_logs(
|
|
|
842
843
|
),
|
|
843
844
|
slug: Optional[str] = typer.Option(
|
|
844
845
|
None,
|
|
845
|
-
'--name',
|
|
846
|
+
'--inference-simulator-name',
|
|
846
847
|
'-isn',
|
|
847
848
|
help="Inference simulator name",
|
|
848
849
|
is_eager=False,
|
|
@@ -952,17 +953,14 @@ def deploy_inference_simulator_model_to_instance(
|
|
|
952
953
|
check_command_permission(command_name)
|
|
953
954
|
|
|
954
955
|
if model_slug and not re.match(r"^[a-zA-Z0-9-]+$", model_slug):
|
|
955
|
-
raise typer.BadParameter(
|
|
956
|
-
|
|
957
|
-
new_inference_simulator_slug = f"{model_slug}-{int(time.time())}"
|
|
956
|
+
raise typer.BadParameter("Invalid name format. Name can only contain letters, numbers, and hyphens.")
|
|
958
957
|
|
|
959
958
|
service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
|
|
960
959
|
project_service = service_factory.get_project_service()
|
|
961
960
|
|
|
962
|
-
project_service.project_deploy_inference_simulator_model_to_instance(
|
|
961
|
+
inference_simulator_public_id = project_service.project_deploy_inference_simulator_model_to_instance(
|
|
963
962
|
model_public_id=model_public_id,
|
|
964
963
|
model_slug=model_slug,
|
|
965
|
-
new_inference_simulator_slug=new_inference_simulator_slug,
|
|
966
964
|
rented_instance_public_id=rented_instance_public_id,
|
|
967
965
|
rented_instance_slug=rented_instance_slug,
|
|
968
966
|
self_hosted_instance_public_id=self_hosted_instance_public_id,
|
|
@@ -973,7 +971,7 @@ def deploy_inference_simulator_model_to_instance(
|
|
|
973
971
|
logging_service: LoggingService = service_factory.get_logging_service()
|
|
974
972
|
|
|
975
973
|
logging_service.stream_inference_simulator_logs_with_controls(
|
|
976
|
-
|
|
974
|
+
public_id=inference_simulator_public_id
|
|
977
975
|
)
|
|
978
976
|
raise typer.Exit(0)
|
|
979
977
|
|
|
@@ -982,14 +980,14 @@ def deploy_inference_simulator_model_to_instance(
|
|
|
982
980
|
def deploy_inference_simulator_model_to_sagemaker(
|
|
983
981
|
model_public_id: Optional[str] = typer.Option(
|
|
984
982
|
None,
|
|
985
|
-
'--id',
|
|
983
|
+
'--model-id',
|
|
986
984
|
'-mid',
|
|
987
985
|
help="Inference simulator model ID",
|
|
988
986
|
is_eager=False,
|
|
989
987
|
),
|
|
990
988
|
model_slug: Optional[str] = typer.Option(
|
|
991
989
|
None,
|
|
992
|
-
'--name',
|
|
990
|
+
'--model-name',
|
|
993
991
|
'-mn',
|
|
994
992
|
help="Inference simulator model name",
|
|
995
993
|
is_eager=False,
|
|
@@ -11,7 +11,7 @@ class ProjectInferenceSimulatorModelEntity(BaseModel):
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
public_id: Optional[str] = Field(None, alias='ID')
|
|
14
|
-
slug: Optional[str] = Field(None, alias='
|
|
14
|
+
slug: Optional[str] = Field(None, alias='NAME')
|
|
15
15
|
status: Optional[str] = Field(None, alias='STATUS')
|
|
16
16
|
commit_hash: Optional[str] = Field(None, alias='COMMIT_HASH')
|
|
17
17
|
environment_metadata: Optional[Dict[str, Any]] = Field(None, alias='ENVIRONMENT_METADATA')
|
|
@@ -106,8 +106,8 @@ def error_handler() -> Callable:
|
|
|
106
106
|
raise e100
|
|
107
107
|
else:
|
|
108
108
|
typer.echo(__('Undefined error occurred'))
|
|
109
|
-
typer.echo(e100.__class__.__name__)
|
|
110
|
-
print(traceback.format_exc())
|
|
109
|
+
# typer.echo(e100.__class__.__name__)
|
|
110
|
+
# print(traceback.format_exc())
|
|
111
111
|
# TODO send all exceptions to backend?
|
|
112
112
|
app_logger.error(f'{traceback.format_exc()}')
|
|
113
113
|
raise typer.Exit(1)
|
|
@@ -19,7 +19,7 @@ def main():
|
|
|
19
19
|
config_provider.update_allowed_commands_and_is_token_valid(validate_token_response=token_info)
|
|
20
20
|
except Exception as e:
|
|
21
21
|
app_logger.error(f'{traceback.format_exc()}')
|
|
22
|
-
print('
|
|
22
|
+
print('Error connecting to TheStage servers') # TODO inquire what we want here if backend is offline
|
|
23
23
|
print(f'Application logs path: {str(get_log_path_from_os())}')
|
|
24
24
|
return
|
|
25
25
|
|
|
@@ -196,7 +196,7 @@ class GitLocalClient:
|
|
|
196
196
|
) -> Optional[str]:
|
|
197
197
|
repo = self.__get_repo(path=path)
|
|
198
198
|
if repo.head.is_detached:
|
|
199
|
-
line_color = ColorScheme.GIT_HEADLESS
|
|
199
|
+
line_color = ColorScheme.GIT_HEADLESS.value
|
|
200
200
|
print(f'[{line_color}]Committing in detached head state at {repo.head.commit.hexsha}[/{line_color}]')
|
|
201
201
|
commit_name = name if name else f"Auto commit {str(datetime.datetime.now().date())}"
|
|
202
202
|
commit = repo.git.commit('--allow-empty', '-m', commit_name, )
|
|
@@ -667,7 +667,6 @@ class TheStageApiClient(TheStageApiClientCore):
|
|
|
667
667
|
self,
|
|
668
668
|
model_public_id: str,
|
|
669
669
|
model_slug: str,
|
|
670
|
-
new_inference_simulator_slug: str,
|
|
671
670
|
rented_instance_public_id: Optional[str] = None,
|
|
672
671
|
rented_instance_slug: Optional[str] = None,
|
|
673
672
|
self_hosted_instance_public_id: Optional[str] = None,
|
|
@@ -675,10 +674,9 @@ class TheStageApiClient(TheStageApiClientCore):
|
|
|
675
674
|
|
|
676
675
|
) -> Optional[DeployInferenceModelToInstanceResponse]:
|
|
677
676
|
request = DeployInferenceModelToInstanceRequest(
|
|
678
|
-
inferenceSimulatorSlug=new_inference_simulator_slug,
|
|
679
677
|
modelPublicId=model_public_id,
|
|
680
678
|
modelSlug=model_slug,
|
|
681
|
-
|
|
679
|
+
instanceRentedPublicId=rented_instance_public_id,
|
|
682
680
|
instanceRentedSlug=rented_instance_slug,
|
|
683
681
|
selfhostedInstancePublicId=self_hosted_instance_public_id,
|
|
684
682
|
selfhostedInstanceSlug=self_hosted_instance_slug
|
|
@@ -735,7 +733,7 @@ class TheStageApiClient(TheStageApiClientCore):
|
|
|
735
733
|
return result
|
|
736
734
|
|
|
737
735
|
|
|
738
|
-
def
|
|
736
|
+
def resolve_user_input(
|
|
739
737
|
self,
|
|
740
738
|
entity_identifier: str
|
|
741
739
|
) -> Optional[ConnectResolveOptionsResponse]:
|
|
@@ -745,7 +743,7 @@ class TheStageApiClient(TheStageApiClientCore):
|
|
|
745
743
|
|
|
746
744
|
response = self._request(
|
|
747
745
|
method='POST',
|
|
748
|
-
url='/user-api/v1/
|
|
746
|
+
url='/user-api/v1/resolve-user-input',
|
|
749
747
|
data=data,
|
|
750
748
|
token=self.__config_provider.get_config().main.thestage_auth_token,
|
|
751
749
|
)
|
|
@@ -11,6 +11,7 @@ class EntityMatchData(BaseModel):
|
|
|
11
11
|
publicId: str = Field(alias='publicId')
|
|
12
12
|
frontendStatus: FrontendStatusDto = Field(alias='frontendStatus')
|
|
13
13
|
canConnect: bool = Field(alias='canConnect')
|
|
14
|
+
canDownloadUploadOnContainer: bool = Field(alias='canDownloadUploadOnContainer')
|
|
14
15
|
matchedField: Optional[str] = Field(None, alias='matchedField')
|
|
15
16
|
|
|
16
17
|
|
|
@@ -16,5 +16,3 @@ class DeployInferenceModelToInstanceRequest(BaseModel):
|
|
|
16
16
|
|
|
17
17
|
selfhostedInstancePublicId: Optional[str] = Field(None, alias='selfhostedInstancePublicId')
|
|
18
18
|
selfhostedInstanceSlug: Optional[str] = Field(None, alias='selfhostedInstanceSlug')
|
|
19
|
-
|
|
20
|
-
inferenceSimulatorSlug: Optional[str] = Field(None, alias='inferenceSimulatorSlug')
|
|
@@ -9,4 +9,6 @@ from thestage.services.project.dto.inference_simulator_model_dto import Inferenc
|
|
|
9
9
|
class DeployInferenceModelToInstanceResponse(TheStageBaseResponse):
|
|
10
10
|
model_config = ConfigDict(use_enum_values=True)
|
|
11
11
|
|
|
12
|
+
inferenceSimulatorPublicId: Optional[str] = Field(None, alias='inferenceSimulatorPublicId')
|
|
13
|
+
|
|
12
14
|
|
|
@@ -8,5 +8,5 @@ from thestage.services.clients.thestage_api.dtos.entity_filter_request import En
|
|
|
8
8
|
class GetInferenceSimulatorRequest(BaseModel):
|
|
9
9
|
model_config = ConfigDict(use_enum_values=True)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
publicId: Optional[str] = Field(None, alias='publicId')
|
|
12
12
|
slug: Optional[str] = Field(None, alias='slug')
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
|
|
1
3
|
from pydantic import Field, ConfigDict
|
|
2
4
|
|
|
3
5
|
from thestage.services.clients.thestage_api.dtos.base_response import TheStageBaseResponse
|
|
@@ -8,3 +10,4 @@ class ProjectRunTaskResponse(TheStageBaseResponse):
|
|
|
8
10
|
model_config = ConfigDict(use_enum_values=True)
|
|
9
11
|
|
|
10
12
|
task: TaskDto = Field(None, alias='task')
|
|
13
|
+
tasksInQueue: Optional[List[TaskDto]] = Field(None, alias='tasksInQueue')
|
|
@@ -45,7 +45,7 @@ class ConnectService(AbstractService):
|
|
|
45
45
|
username: Optional[str],
|
|
46
46
|
private_key_path: Optional[str],
|
|
47
47
|
):
|
|
48
|
-
resolved_options = self.__thestage_api_client.
|
|
48
|
+
resolved_options = self.__thestage_api_client.resolve_user_input(entity_identifier=input_entity_identifier)
|
|
49
49
|
entities_available_for_connect_count = 0
|
|
50
50
|
task_presence = False
|
|
51
51
|
container_presence = False
|
|
@@ -56,7 +56,7 @@ class ConnectService(AbstractService):
|
|
|
56
56
|
if resolved_options.taskMatchData:
|
|
57
57
|
for task_item in resolved_options.taskMatchData:
|
|
58
58
|
message = f"Found a task with with matching {task_item.matchedField} in status: '{task_item.frontendStatus.status_translation}' (ID: {task_item.publicId})"
|
|
59
|
-
line_color = ColorScheme.SUCCESS if task_item.canConnect else 'default'
|
|
59
|
+
line_color = ColorScheme.SUCCESS.value if task_item.canConnect else 'default'
|
|
60
60
|
print(f"[{line_color}]{message}[{line_color}]")
|
|
61
61
|
if task_item.canConnect:
|
|
62
62
|
task_presence = True
|
|
@@ -66,7 +66,7 @@ class ConnectService(AbstractService):
|
|
|
66
66
|
if resolved_options.dockerContainerMatchData:
|
|
67
67
|
for container_item in resolved_options.dockerContainerMatchData:
|
|
68
68
|
message = f"Found a container with matching {container_item.matchedField} in status: '{container_item.frontendStatus.status_translation}' (ID: {container_item.publicId})"
|
|
69
|
-
line_color = ColorScheme.SUCCESS if container_item.canConnect else 'default'
|
|
69
|
+
line_color = ColorScheme.SUCCESS.value if container_item.canConnect else 'default'
|
|
70
70
|
print(f"[{line_color}]{message}[{line_color}]")
|
|
71
71
|
if container_item.canConnect:
|
|
72
72
|
container_presence = True
|
|
@@ -76,7 +76,7 @@ class ConnectService(AbstractService):
|
|
|
76
76
|
if resolved_options.instanceRentedMatchData:
|
|
77
77
|
for instance_rented_item in resolved_options.instanceRentedMatchData:
|
|
78
78
|
message = f"Found a rented instance with matching {instance_rented_item.matchedField} in status: '{instance_rented_item.frontendStatus.status_translation}' (ID: {instance_rented_item.publicId})"
|
|
79
|
-
line_color = ColorScheme.SUCCESS if instance_rented_item.canConnect else 'default'
|
|
79
|
+
line_color = ColorScheme.SUCCESS.value if instance_rented_item.canConnect else 'default'
|
|
80
80
|
print(f"[{line_color}]{message}[{line_color}]")
|
|
81
81
|
|
|
82
82
|
if instance_rented_item.canConnect:
|
|
@@ -87,7 +87,7 @@ class ConnectService(AbstractService):
|
|
|
87
87
|
if resolved_options.selfhostedInstanceMatchData:
|
|
88
88
|
for selfhosted_item in resolved_options.selfhostedInstanceMatchData:
|
|
89
89
|
message = f"Found a self-hosted instance with matching {selfhosted_item.matchedField} in status: '{selfhosted_item.frontendStatus.status_translation}' (ID: {selfhosted_item.publicId})"
|
|
90
|
-
line_color = ColorScheme.SUCCESS if selfhosted_item.canConnect else 'default'
|
|
90
|
+
line_color = ColorScheme.SUCCESS.value if selfhosted_item.canConnect else 'default'
|
|
91
91
|
print(f"[{line_color}]{message}[{line_color}]")
|
|
92
92
|
|
|
93
93
|
if selfhosted_item.canConnect:
|
|
@@ -153,7 +153,7 @@ class ConnectService(AbstractService):
|
|
|
153
153
|
|
|
154
154
|
# if no instances found - exit 1
|
|
155
155
|
if instance_rented is None:
|
|
156
|
-
typer.echo(f"No rented instance found with matching
|
|
156
|
+
typer.echo(f"No rented instance found with matching identifier")
|
|
157
157
|
raise typer.Exit(1)
|
|
158
158
|
|
|
159
159
|
note_to_send: Optional[str] = None
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
from typing import List, Tuple, Optional, Dict
|
|
4
|
+
from rich import print
|
|
3
5
|
|
|
4
6
|
import typer
|
|
7
|
+
|
|
8
|
+
from thestage.color_scheme.color_scheme import ColorScheme
|
|
5
9
|
from thestage.entities.container import DockerContainerEntity
|
|
6
10
|
from thestage.services.clients.thestage_api.dtos.container_param_request import DockerContainerActionRequestDto
|
|
7
11
|
from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
|
|
@@ -150,7 +154,7 @@ class ContainerService(AbstractService):
|
|
|
150
154
|
if private_key_path:
|
|
151
155
|
typer.echo(f'Using configured private key for {ip_address}: {private_key_path}')
|
|
152
156
|
else:
|
|
153
|
-
typer.echo(f'Using SSH agent to connect to {ip_address}')
|
|
157
|
+
typer.echo(f'Using SSH agent to connect to {ip_address} as {username}')
|
|
154
158
|
else:
|
|
155
159
|
self.__config_provider.update_remote_server_config_entry(ip_address, Path(private_key_path))
|
|
156
160
|
typer.echo(f'Updated private key path for {ip_address}: {private_key_path}')
|
|
@@ -267,22 +271,49 @@ class ContainerService(AbstractService):
|
|
|
267
271
|
@error_handler()
|
|
268
272
|
def put_file_to_container(
|
|
269
273
|
self,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
destination_path: Optional[str] = None,
|
|
274
|
-
username_param: Optional[str] = None,
|
|
274
|
+
source_path: str,
|
|
275
|
+
destination: str,
|
|
276
|
+
username_param: Optional[str],
|
|
275
277
|
):
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", destination)
|
|
279
|
+
if container_args is None:
|
|
280
|
+
typer.echo(__('Container name and source file path are required as the second argument'))
|
|
281
|
+
typer.echo(__('Example: container_name:/path/to/file'))
|
|
278
282
|
raise typer.Exit(1)
|
|
283
|
+
container_identifier = container_args.groups()[0]
|
|
284
|
+
destination_path = container_args.groups()[1].rstrip("/")
|
|
279
285
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
286
|
+
if not container_identifier:
|
|
287
|
+
typer.echo('Container identifier (container_id_or_name) is required')
|
|
288
|
+
raise typer.Exit(1)
|
|
289
|
+
|
|
290
|
+
resolved_options = self.__thestage_api_client.resolve_user_input(entity_identifier=container_identifier)
|
|
291
|
+
container_public_id = None
|
|
292
|
+
valid_container_count = 0
|
|
293
|
+
for container_item in resolved_options.dockerContainerMatchData:
|
|
294
|
+
message = f"Found a container with matching {container_item.matchedField} in status: '{container_item.frontendStatus.status_translation}' (ID: {container_item.publicId})"
|
|
295
|
+
line_color = ColorScheme.SUCCESS.value if container_item.canDownloadUploadOnContainer else 'default'
|
|
296
|
+
print(f"[{line_color}]{message}[{line_color}]")
|
|
297
|
+
if container_item.canDownloadUploadOnContainer:
|
|
298
|
+
valid_container_count += 1
|
|
299
|
+
container_public_id = container_item.publicId
|
|
300
|
+
|
|
301
|
+
if valid_container_count != 1:
|
|
302
|
+
typer.echo(f"Failed to resolve the container by provided identifier, as total of {valid_container_count} containers are valid options")
|
|
303
|
+
raise typer.Exit(1)
|
|
304
|
+
|
|
305
|
+
container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
|
|
306
|
+
container_public_id=container_public_id,
|
|
284
307
|
)
|
|
285
308
|
|
|
309
|
+
if not container:
|
|
310
|
+
typer.echo(f"Unexpected error: container '{container_public_id}' not found")
|
|
311
|
+
raise typer.Exit(1)
|
|
312
|
+
|
|
313
|
+
if not self.__file_system_service.check_if_path_exist(file=source_path):
|
|
314
|
+
typer.echo(__("File not found at specified path"))
|
|
315
|
+
raise typer.Exit(1)
|
|
316
|
+
|
|
286
317
|
if not container.mappings or not container.mappings.directory_mappings:
|
|
287
318
|
typer.echo(__("Mapping folders not found"))
|
|
288
319
|
raise typer.Exit(1)
|
|
@@ -296,10 +327,18 @@ class ContainerService(AbstractService):
|
|
|
296
327
|
typer.echo(__("Cannot find matching container volume mapping for specified file path"))
|
|
297
328
|
raise typer.Exit(1)
|
|
298
329
|
|
|
330
|
+
username, ip_address, private_key_path = self.get_server_auth(
|
|
331
|
+
container=container,
|
|
332
|
+
username_param=username_param,
|
|
333
|
+
private_key_path_override=None
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
copy_only_folder_contents = source_path.endswith("/")
|
|
337
|
+
|
|
299
338
|
self.__remote_server_service.upload_data_to_container(
|
|
300
339
|
ip_address=ip_address,
|
|
301
340
|
username=username,
|
|
302
|
-
src_path=
|
|
341
|
+
src_path=source_path,
|
|
303
342
|
dest_path=destination_path,
|
|
304
343
|
instance_path=instance_path,
|
|
305
344
|
container_path=container_path,
|
|
@@ -310,31 +349,67 @@ class ContainerService(AbstractService):
|
|
|
310
349
|
@error_handler()
|
|
311
350
|
def get_file_from_container(
|
|
312
351
|
self,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
copy_only_folder_contents: bool,
|
|
316
|
-
destination_path: Optional[str] = None,
|
|
352
|
+
source: str,
|
|
353
|
+
destination_path: str,
|
|
317
354
|
username_param: Optional[str] = None,
|
|
318
355
|
):
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
356
|
+
container_args = re.match(r"^([\w\W]+?):([\w\W]+)$", source)
|
|
357
|
+
|
|
358
|
+
if container_args is None:
|
|
359
|
+
typer.echo(__('Container name and source directory path are required as the first argument'))
|
|
360
|
+
typer.echo(__('Example: container_name:/path/to/file'))
|
|
361
|
+
raise typer.Exit(1)
|
|
362
|
+
container_identifier = container_args.groups()[0]
|
|
363
|
+
source_path = container_args.groups()[1]
|
|
364
|
+
|
|
365
|
+
if not container_identifier:
|
|
366
|
+
typer.echo('Container identifier (container_id_or_name) is required')
|
|
367
|
+
raise typer.Exit(1)
|
|
368
|
+
|
|
369
|
+
resolved_options = self.__thestage_api_client.resolve_user_input(entity_identifier=container_identifier)
|
|
370
|
+
container_public_id = None
|
|
371
|
+
valid_container_count = 0
|
|
372
|
+
for container_item in resolved_options.dockerContainerMatchData:
|
|
373
|
+
message = f"Found a container with matching {container_item.matchedField} in status: '{container_item.frontendStatus.status_translation}' (ID: {container_item.publicId})"
|
|
374
|
+
line_color = ColorScheme.SUCCESS.value if container_item.canDownloadUploadOnContainer else 'default'
|
|
375
|
+
print(f"[{line_color}]{message}[{line_color}]")
|
|
376
|
+
if container_item.canDownloadUploadOnContainer:
|
|
377
|
+
valid_container_count += 1
|
|
378
|
+
container_public_id = container_item.publicId
|
|
379
|
+
|
|
380
|
+
if valid_container_count != 1:
|
|
381
|
+
typer.echo(f"Failed to resolve the container by provided identifier, as total of {valid_container_count} containers are valid options")
|
|
382
|
+
raise typer.Exit(1)
|
|
383
|
+
|
|
384
|
+
container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
|
|
385
|
+
container_public_id=container_public_id,
|
|
323
386
|
)
|
|
324
387
|
|
|
388
|
+
if not container:
|
|
389
|
+
typer.echo(f"Unexpected error: container '{container_public_id}' not found")
|
|
390
|
+
raise typer.Exit(1)
|
|
391
|
+
|
|
325
392
|
if not container.mappings or not container.mappings.directory_mappings:
|
|
326
393
|
typer.echo(__("Mapping folders not found"))
|
|
327
394
|
raise typer.Exit(1)
|
|
328
395
|
|
|
329
396
|
instance_path, container_path = self._get_new_path_from_mapping(
|
|
330
397
|
directory_mapping=container.mappings.directory_mappings,
|
|
331
|
-
destination_path=
|
|
398
|
+
destination_path=source_path,
|
|
332
399
|
)
|
|
333
400
|
|
|
334
401
|
if not instance_path and not container_path:
|
|
335
402
|
typer.echo(__("Cannot find matching container volume mapping for specified file path"))
|
|
336
403
|
raise typer.Exit(1)
|
|
337
404
|
|
|
405
|
+
username, ip_address, private_key_path = self.get_server_auth(
|
|
406
|
+
container=container,
|
|
407
|
+
username_param=username_param,
|
|
408
|
+
private_key_path_override=None,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
copy_only_folder_contents=source_path.endswith("/")
|
|
412
|
+
|
|
338
413
|
self.__remote_server_service.download_data_from_container(
|
|
339
414
|
ip_address=ip_address,
|
|
340
415
|
username=username,
|