pybiolib 0.2.951__py3-none-any.whl → 1.2.1890__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.
- biolib/__init__.py +357 -11
- biolib/_data_record/data_record.py +380 -0
- biolib/_index/__init__.py +0 -0
- biolib/_index/index.py +55 -0
- biolib/_index/query_result.py +103 -0
- biolib/_internal/__init__.py +0 -0
- biolib/_internal/add_copilot_prompts.py +58 -0
- biolib/_internal/add_gui_files.py +81 -0
- biolib/_internal/data_record/__init__.py +1 -0
- biolib/_internal/data_record/data_record.py +85 -0
- biolib/_internal/data_record/push_data.py +116 -0
- biolib/_internal/data_record/remote_storage_endpoint.py +43 -0
- biolib/_internal/errors.py +5 -0
- biolib/_internal/file_utils.py +125 -0
- biolib/_internal/fuse_mount/__init__.py +1 -0
- biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
- biolib/_internal/http_client.py +159 -0
- biolib/_internal/lfs/__init__.py +1 -0
- biolib/_internal/lfs/cache.py +51 -0
- biolib/_internal/libs/__init__.py +1 -0
- biolib/_internal/libs/fusepy/__init__.py +1257 -0
- biolib/_internal/push_application.py +488 -0
- biolib/_internal/runtime.py +22 -0
- biolib/_internal/string_utils.py +13 -0
- biolib/_internal/templates/__init__.py +1 -0
- biolib/_internal/templates/copilot_template/.github/instructions/general-app-knowledge.instructions.md +10 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-general.instructions.md +20 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-python.instructions.md +16 -0
- biolib/_internal/templates/copilot_template/.github/instructions/style-react-ts.instructions.md +47 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_app_inputs.prompt.md +11 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_onboard_repo.prompt.md +19 -0
- biolib/_internal/templates/copilot_template/.github/prompts/biolib_run_apps.prompt.md +12 -0
- biolib/_internal/templates/dashboard_template/.biolib/config.yml +5 -0
- biolib/_internal/templates/github_workflow_template/.github/workflows/biolib.yml +21 -0
- biolib/_internal/templates/gitignore_template/.gitignore +10 -0
- biolib/_internal/templates/gui_template/.yarnrc.yml +1 -0
- biolib/_internal/templates/gui_template/App.tsx +53 -0
- biolib/_internal/templates/gui_template/Dockerfile +27 -0
- biolib/_internal/templates/gui_template/biolib-sdk.ts +82 -0
- biolib/_internal/templates/gui_template/dev-data/output.json +7 -0
- biolib/_internal/templates/gui_template/index.css +5 -0
- biolib/_internal/templates/gui_template/index.html +13 -0
- biolib/_internal/templates/gui_template/index.tsx +10 -0
- biolib/_internal/templates/gui_template/package.json +27 -0
- biolib/_internal/templates/gui_template/tsconfig.json +24 -0
- biolib/_internal/templates/gui_template/vite-plugin-dev-data.ts +50 -0
- biolib/_internal/templates/gui_template/vite.config.mts +10 -0
- biolib/_internal/templates/init_template/.biolib/config.yml +19 -0
- biolib/_internal/templates/init_template/Dockerfile +14 -0
- biolib/_internal/templates/init_template/requirements.txt +1 -0
- biolib/_internal/templates/init_template/run.py +12 -0
- biolib/_internal/templates/init_template/run.sh +4 -0
- biolib/_internal/templates/templates.py +25 -0
- biolib/_internal/tree_utils.py +106 -0
- biolib/_internal/utils/__init__.py +65 -0
- biolib/_internal/utils/auth.py +46 -0
- biolib/_internal/utils/job_url.py +33 -0
- biolib/_internal/utils/multinode.py +263 -0
- biolib/_runtime/runtime.py +157 -0
- biolib/_session/session.py +44 -0
- biolib/_shared/__init__.py +0 -0
- biolib/_shared/types/__init__.py +74 -0
- biolib/_shared/types/account.py +12 -0
- biolib/_shared/types/account_member.py +8 -0
- biolib/_shared/types/app.py +9 -0
- biolib/_shared/types/data_record.py +40 -0
- biolib/_shared/types/experiment.py +32 -0
- biolib/_shared/types/file_node.py +17 -0
- biolib/_shared/types/push.py +6 -0
- biolib/_shared/types/resource.py +37 -0
- biolib/_shared/types/resource_deploy_key.py +11 -0
- biolib/_shared/types/resource_permission.py +14 -0
- biolib/_shared/types/resource_version.py +19 -0
- biolib/_shared/types/result.py +14 -0
- biolib/_shared/types/typing.py +10 -0
- biolib/_shared/types/user.py +19 -0
- biolib/_shared/utils/__init__.py +7 -0
- biolib/_shared/utils/resource_uri.py +75 -0
- biolib/api/__init__.py +6 -0
- biolib/api/client.py +168 -0
- biolib/app/app.py +252 -49
- biolib/app/search_apps.py +45 -0
- biolib/biolib_api_client/api_client.py +126 -31
- biolib/biolib_api_client/app_types.py +24 -4
- biolib/biolib_api_client/auth.py +31 -8
- biolib/biolib_api_client/biolib_app_api.py +147 -52
- biolib/biolib_api_client/biolib_job_api.py +161 -141
- biolib/biolib_api_client/job_types.py +21 -5
- biolib/biolib_api_client/lfs_types.py +7 -23
- biolib/biolib_api_client/user_state.py +56 -0
- biolib/biolib_binary_format/__init__.py +1 -4
- biolib/biolib_binary_format/file_in_container.py +105 -0
- biolib/biolib_binary_format/module_input.py +24 -7
- biolib/biolib_binary_format/module_output_v2.py +149 -0
- biolib/biolib_binary_format/remote_endpoints.py +34 -0
- biolib/biolib_binary_format/remote_stream_seeker.py +59 -0
- biolib/biolib_binary_format/saved_job.py +3 -2
- biolib/biolib_binary_format/{attestation_document.py → stdout_and_stderr.py} +8 -8
- biolib/biolib_binary_format/system_status_update.py +3 -2
- biolib/biolib_binary_format/utils.py +175 -0
- biolib/biolib_docker_client/__init__.py +11 -2
- biolib/biolib_errors.py +36 -0
- biolib/biolib_logging.py +27 -10
- biolib/cli/__init__.py +38 -0
- biolib/cli/auth.py +46 -0
- biolib/cli/data_record.py +164 -0
- biolib/cli/index.py +32 -0
- biolib/cli/init.py +421 -0
- biolib/cli/lfs.py +101 -0
- biolib/cli/push.py +50 -0
- biolib/cli/run.py +63 -0
- biolib/cli/runtime.py +14 -0
- biolib/cli/sdk.py +16 -0
- biolib/cli/start.py +56 -0
- biolib/compute_node/cloud_utils/cloud_utils.py +110 -161
- biolib/compute_node/job_worker/cache_state.py +66 -88
- biolib/compute_node/job_worker/cache_types.py +1 -6
- biolib/compute_node/job_worker/docker_image_cache.py +112 -37
- biolib/compute_node/job_worker/executors/__init__.py +0 -3
- biolib/compute_node/job_worker/executors/docker_executor.py +532 -199
- biolib/compute_node/job_worker/executors/docker_types.py +9 -1
- biolib/compute_node/job_worker/executors/types.py +19 -9
- biolib/compute_node/job_worker/job_legacy_input_wait_timeout_thread.py +30 -0
- biolib/compute_node/job_worker/job_max_runtime_timer_thread.py +3 -5
- biolib/compute_node/job_worker/job_storage.py +108 -0
- biolib/compute_node/job_worker/job_worker.py +397 -212
- biolib/compute_node/job_worker/large_file_system.py +87 -38
- biolib/compute_node/job_worker/network_alloc.py +99 -0
- biolib/compute_node/job_worker/network_buffer.py +240 -0
- biolib/compute_node/job_worker/utilization_reporter_thread.py +197 -0
- biolib/compute_node/job_worker/utils.py +9 -24
- biolib/compute_node/remote_host_proxy.py +400 -98
- biolib/compute_node/utils.py +31 -9
- biolib/compute_node/webserver/compute_node_results_proxy.py +189 -0
- biolib/compute_node/webserver/proxy_utils.py +28 -0
- biolib/compute_node/webserver/webserver.py +130 -44
- biolib/compute_node/webserver/webserver_types.py +2 -6
- biolib/compute_node/webserver/webserver_utils.py +77 -12
- biolib/compute_node/webserver/worker_thread.py +183 -42
- biolib/experiments/__init__.py +0 -0
- biolib/experiments/experiment.py +356 -0
- biolib/jobs/__init__.py +1 -0
- biolib/jobs/job.py +741 -0
- biolib/jobs/job_result.py +185 -0
- biolib/jobs/types.py +50 -0
- biolib/py.typed +0 -0
- biolib/runtime/__init__.py +14 -0
- biolib/sdk/__init__.py +91 -0
- biolib/tables.py +34 -0
- biolib/typing_utils.py +2 -7
- biolib/user/__init__.py +1 -0
- biolib/user/sign_in.py +54 -0
- biolib/utils/__init__.py +162 -0
- biolib/utils/cache_state.py +94 -0
- biolib/utils/multipart_uploader.py +194 -0
- biolib/utils/seq_util.py +150 -0
- biolib/utils/zip/remote_zip.py +640 -0
- pybiolib-1.2.1890.dist-info/METADATA +41 -0
- pybiolib-1.2.1890.dist-info/RECORD +177 -0
- {pybiolib-0.2.951.dist-info → pybiolib-1.2.1890.dist-info}/WHEEL +1 -1
- pybiolib-1.2.1890.dist-info/entry_points.txt +2 -0
- README.md +0 -17
- biolib/app/app_result.py +0 -68
- biolib/app/utils.py +0 -62
- biolib/biolib-js/0-biolib.worker.js +0 -1
- biolib/biolib-js/1-biolib.worker.js +0 -1
- biolib/biolib-js/2-biolib.worker.js +0 -1
- biolib/biolib-js/3-biolib.worker.js +0 -1
- biolib/biolib-js/4-biolib.worker.js +0 -1
- biolib/biolib-js/5-biolib.worker.js +0 -1
- biolib/biolib-js/6-biolib.worker.js +0 -1
- biolib/biolib-js/index.html +0 -10
- biolib/biolib-js/main-biolib.js +0 -1
- biolib/biolib_api_client/biolib_account_api.py +0 -21
- biolib/biolib_api_client/biolib_large_file_system_api.py +0 -108
- biolib/biolib_binary_format/aes_encrypted_package.py +0 -42
- biolib/biolib_binary_format/module_output.py +0 -58
- biolib/biolib_binary_format/rsa_encrypted_aes_package.py +0 -57
- biolib/biolib_push.py +0 -114
- biolib/cli.py +0 -203
- biolib/cli_utils.py +0 -273
- biolib/compute_node/cloud_utils/enclave_parent_types.py +0 -7
- biolib/compute_node/enclave/__init__.py +0 -2
- biolib/compute_node/enclave/enclave_remote_hosts.py +0 -53
- biolib/compute_node/enclave/nitro_secure_module_utils.py +0 -64
- biolib/compute_node/job_worker/executors/base_executor.py +0 -18
- biolib/compute_node/job_worker/executors/pyppeteer_executor.py +0 -173
- biolib/compute_node/job_worker/executors/remote/__init__.py +0 -1
- biolib/compute_node/job_worker/executors/remote/nitro_enclave_utils.py +0 -81
- biolib/compute_node/job_worker/executors/remote/remote_executor.py +0 -51
- biolib/lfs.py +0 -196
- biolib/pyppeteer/.circleci/config.yml +0 -100
- biolib/pyppeteer/.coveragerc +0 -3
- biolib/pyppeteer/.gitignore +0 -89
- biolib/pyppeteer/.pre-commit-config.yaml +0 -28
- biolib/pyppeteer/CHANGES.md +0 -253
- biolib/pyppeteer/CONTRIBUTING.md +0 -26
- biolib/pyppeteer/LICENSE +0 -12
- biolib/pyppeteer/README.md +0 -137
- biolib/pyppeteer/docs/Makefile +0 -177
- biolib/pyppeteer/docs/_static/custom.css +0 -28
- biolib/pyppeteer/docs/_templates/layout.html +0 -10
- biolib/pyppeteer/docs/changes.md +0 -1
- biolib/pyppeteer/docs/conf.py +0 -299
- biolib/pyppeteer/docs/index.md +0 -21
- biolib/pyppeteer/docs/make.bat +0 -242
- biolib/pyppeteer/docs/reference.md +0 -211
- biolib/pyppeteer/docs/server.py +0 -60
- biolib/pyppeteer/poetry.lock +0 -1699
- biolib/pyppeteer/pyppeteer/__init__.py +0 -135
- biolib/pyppeteer/pyppeteer/accessibility.py +0 -286
- biolib/pyppeteer/pyppeteer/browser.py +0 -401
- biolib/pyppeteer/pyppeteer/browser_fetcher.py +0 -194
- biolib/pyppeteer/pyppeteer/command.py +0 -22
- biolib/pyppeteer/pyppeteer/connection/__init__.py +0 -242
- biolib/pyppeteer/pyppeteer/connection/cdpsession.py +0 -101
- biolib/pyppeteer/pyppeteer/coverage.py +0 -346
- biolib/pyppeteer/pyppeteer/device_descriptors.py +0 -787
- biolib/pyppeteer/pyppeteer/dialog.py +0 -79
- biolib/pyppeteer/pyppeteer/domworld.py +0 -597
- biolib/pyppeteer/pyppeteer/emulation_manager.py +0 -53
- biolib/pyppeteer/pyppeteer/errors.py +0 -48
- biolib/pyppeteer/pyppeteer/events.py +0 -63
- biolib/pyppeteer/pyppeteer/execution_context.py +0 -156
- biolib/pyppeteer/pyppeteer/frame/__init__.py +0 -299
- biolib/pyppeteer/pyppeteer/frame/frame_manager.py +0 -306
- biolib/pyppeteer/pyppeteer/helpers.py +0 -245
- biolib/pyppeteer/pyppeteer/input.py +0 -371
- biolib/pyppeteer/pyppeteer/jshandle.py +0 -598
- biolib/pyppeteer/pyppeteer/launcher.py +0 -683
- biolib/pyppeteer/pyppeteer/lifecycle_watcher.py +0 -169
- biolib/pyppeteer/pyppeteer/models/__init__.py +0 -103
- biolib/pyppeteer/pyppeteer/models/_protocol.py +0 -12460
- biolib/pyppeteer/pyppeteer/multimap.py +0 -82
- biolib/pyppeteer/pyppeteer/network_manager.py +0 -678
- biolib/pyppeteer/pyppeteer/options.py +0 -8
- biolib/pyppeteer/pyppeteer/page.py +0 -1728
- biolib/pyppeteer/pyppeteer/pipe_transport.py +0 -59
- biolib/pyppeteer/pyppeteer/target.py +0 -147
- biolib/pyppeteer/pyppeteer/task_queue.py +0 -24
- biolib/pyppeteer/pyppeteer/timeout_settings.py +0 -36
- biolib/pyppeteer/pyppeteer/tracing.py +0 -93
- biolib/pyppeteer/pyppeteer/us_keyboard_layout.py +0 -305
- biolib/pyppeteer/pyppeteer/util.py +0 -18
- biolib/pyppeteer/pyppeteer/websocket_transport.py +0 -47
- biolib/pyppeteer/pyppeteer/worker.py +0 -101
- biolib/pyppeteer/pyproject.toml +0 -97
- biolib/pyppeteer/spell.txt +0 -137
- biolib/pyppeteer/tox.ini +0 -72
- biolib/pyppeteer/utils/generate_protocol_types.py +0 -603
- biolib/start_cli.py +0 -7
- biolib/utils.py +0 -47
- biolib/validators/validate_app_version.py +0 -183
- biolib/validators/validate_argument.py +0 -134
- biolib/validators/validate_module.py +0 -323
- biolib/validators/validate_zip_file.py +0 -40
- biolib/validators/validator_utils.py +0 -103
- pybiolib-0.2.951.dist-info/LICENSE +0 -21
- pybiolib-0.2.951.dist-info/METADATA +0 -61
- pybiolib-0.2.951.dist-info/RECORD +0 -153
- pybiolib-0.2.951.dist-info/entry_points.txt +0 -3
- /LICENSE → /pybiolib-1.2.1890.dist-info/licenses/LICENSE +0 -0
biolib/cli/start.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import platform
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from biolib.biolib_logging import logger, logger_no_user_data
|
|
8
|
+
from biolib.typing_utils import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command(help='Start a local compute node', hidden=True)
|
|
12
|
+
@click.option('--host', default='127.0.0.1', required=False) # TODO: Validate host
|
|
13
|
+
@click.option('--port', default=5000, type=click.IntRange(1, 65_535), required=False)
|
|
14
|
+
@click.option('--tls-certificate', type=click.Path(exists=True), required=False, hidden=True)
|
|
15
|
+
@click.option('--tls-key', type=click.Path(exists=True), required=False, hidden=True)
|
|
16
|
+
@click.option('--initialize-network-buffer', is_flag=True, help='Initialize the remote host network buffer and exit')
|
|
17
|
+
def start(
|
|
18
|
+
host: str, port: int, tls_certificate: Optional[str], tls_key: Optional[str], initialize_network_buffer: bool
|
|
19
|
+
) -> None:
|
|
20
|
+
logger.configure(default_log_level=logging.INFO)
|
|
21
|
+
logger_no_user_data.configure(default_log_level=logging.INFO)
|
|
22
|
+
if platform.system() == 'Windows':
|
|
23
|
+
raise Exception('Starting a compute node is currently not supported on Windows')
|
|
24
|
+
|
|
25
|
+
if tls_certificate and not tls_key or tls_key and not tls_certificate:
|
|
26
|
+
raise Exception('Options --tls-certificate and --tls-key must be specified together')
|
|
27
|
+
|
|
28
|
+
if initialize_network_buffer:
|
|
29
|
+
from biolib.compute_node.job_worker.network_buffer import ( # pylint: disable=import-outside-toplevel
|
|
30
|
+
NetworkBuffer,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
network_buffer = NetworkBuffer.get_instance()
|
|
34
|
+
created = network_buffer.fill_buffer()
|
|
35
|
+
logger_no_user_data.info(f'Initialized network buffer (created {created} networks)')
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from biolib.compute_node.webserver import webserver # pylint: disable=import-outside-toplevel
|
|
40
|
+
|
|
41
|
+
webserver.start_webserver(
|
|
42
|
+
host=host,
|
|
43
|
+
port=port,
|
|
44
|
+
tls_pem_key_path=tls_key,
|
|
45
|
+
tls_pem_certificate_path=tls_certificate,
|
|
46
|
+
)
|
|
47
|
+
except ModuleNotFoundError as error:
|
|
48
|
+
if error.name in ('flask', 'gunicorn'):
|
|
49
|
+
print(
|
|
50
|
+
'To use this command, please install the compute-node extras with '
|
|
51
|
+
'"pip3 install --upgrade pybiolib[compute-node]"',
|
|
52
|
+
file=sys.stderr,
|
|
53
|
+
)
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
raise error
|
|
@@ -1,187 +1,138 @@
|
|
|
1
1
|
# pylint: disable=unsubscriptable-object
|
|
2
|
-
|
|
2
|
+
import base64
|
|
3
|
+
import json
|
|
3
4
|
import os
|
|
4
5
|
import subprocess
|
|
5
6
|
import time
|
|
6
|
-
import signal
|
|
7
|
-
from multiprocessing import Process
|
|
8
7
|
from datetime import datetime
|
|
9
8
|
from socket import gethostbyname, gethostname
|
|
10
|
-
import requests
|
|
11
9
|
|
|
12
|
-
from biolib import utils
|
|
13
|
-
from biolib.biolib_errors import BioLibError
|
|
14
|
-
from biolib.biolib_logging import logger
|
|
15
|
-
from biolib.typing_utils import Optional
|
|
10
|
+
from biolib import api, utils
|
|
16
11
|
from biolib.biolib_api_client import BiolibApiClient
|
|
17
|
-
from biolib.
|
|
18
|
-
from biolib.compute_node.webserver.webserver_types import
|
|
19
|
-
from biolib.
|
|
20
|
-
|
|
12
|
+
from biolib.biolib_logging import logger_no_user_data
|
|
13
|
+
from biolib.compute_node.webserver.webserver_types import ComputeNodeInfo, ShutdownTimes, WebserverConfig
|
|
14
|
+
from biolib.typing_utils import Dict, List, Optional, cast
|
|
21
15
|
|
|
22
|
-
class CloudAutoShutdownTimer(Process):
|
|
23
|
-
def __init__(self, seconds_to_wait: int) -> None:
|
|
24
|
-
super().__init__()
|
|
25
|
-
self._seconds_to_wait = seconds_to_wait
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
CloudUtils.deregister_and_shutdown()
|
|
17
|
+
def trust_ceritificates(certs_data: List[str]) -> None:
|
|
18
|
+
ca_directory_amazon_linux_2 = '/etc/pki/ca-trust/source/anchors/'
|
|
30
19
|
|
|
20
|
+
if not os.path.exists(ca_directory_amazon_linux_2):
|
|
21
|
+
logger_no_user_data.error(f'Certificate path not found at {ca_directory_amazon_linux_2}')
|
|
22
|
+
return
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@staticmethod
|
|
36
|
-
def get_webserver_config():
|
|
37
|
-
response = requests.get(f'{_EnclaveUtils._BASE_URL}/config/', timeout=5)
|
|
38
|
-
return response.json()
|
|
39
|
-
|
|
40
|
-
@staticmethod
|
|
41
|
-
def deregister_and_shutdown() -> None:
|
|
42
|
-
requests.post(url=f'{_EnclaveUtils._BASE_URL}/deregister_and_shutdown/', timeout=5)
|
|
24
|
+
for idx, cert_data in enumerate(certs_data):
|
|
25
|
+
with open(f'{ca_directory_amazon_linux_2}bl-cert-{idx}.crt', mode='w') as cert_file:
|
|
26
|
+
cert_file.write(cert_data)
|
|
43
27
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@staticmethod
|
|
51
|
-
def stop_vsock_proxy(vsock_proxy_id: str) -> None:
|
|
52
|
-
requests.delete(url=f'{_EnclaveUtils._BASE_URL}/vsock_proxy/{vsock_proxy_id}/', timeout=5)
|
|
53
|
-
|
|
54
|
-
@staticmethod
|
|
55
|
-
def log_message_to_log_file(log_message: str, level: int) -> None:
|
|
56
|
-
requests.post(
|
|
57
|
-
url=f'{_EnclaveUtils._BASE_URL}/log/',
|
|
58
|
-
json={
|
|
59
|
-
'log_message': log_message,
|
|
60
|
-
'level': level
|
|
61
|
-
},
|
|
62
|
-
timeout=5,
|
|
63
|
-
)
|
|
28
|
+
result = subprocess.run(['update-ca-trust'], capture_output=True, check=False)
|
|
29
|
+
logger_no_user_data.debug(result.stdout.decode())
|
|
30
|
+
if result.returncode == 0:
|
|
31
|
+
logger_no_user_data.info('Certificates added successfully!')
|
|
32
|
+
else:
|
|
33
|
+
logger_no_user_data.error(f'Failed to update certificates: {result.stderr.decode()}')
|
|
64
34
|
|
|
65
35
|
|
|
66
36
|
class CloudUtils:
|
|
67
37
|
_webserver_config: Optional[WebserverConfig] = None
|
|
68
|
-
enclave = _EnclaveUtils
|
|
69
|
-
_auto_shutdown_timer_pid_file_name: str = '/tmp/biolib_auto_shutdown_timer.pid'
|
|
70
38
|
|
|
71
39
|
@staticmethod
|
|
72
40
|
def initialize() -> None:
|
|
73
|
-
|
|
41
|
+
logger_no_user_data.info('Reporting availability...')
|
|
74
42
|
CloudUtils._report_availability()
|
|
75
|
-
CloudUtils.start_auto_shutdown_timer()
|
|
76
43
|
|
|
77
44
|
@staticmethod
|
|
78
45
|
def get_webserver_config() -> WebserverConfig:
|
|
79
46
|
if CloudUtils._webserver_config:
|
|
80
47
|
return CloudUtils._webserver_config
|
|
81
48
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
s3_general_storage_bucket_name=CloudUtils._get_environment_variable(
|
|
95
|
-
'BIOLIB_S3_GENERAL_STORAGE_BUCKET_NAME'
|
|
49
|
+
CloudUtils._webserver_config = WebserverConfig(
|
|
50
|
+
compute_node_info=ComputeNodeInfo(
|
|
51
|
+
auth_token=CloudUtils._get_environment_variable_or_fail('BIOLIB_COMPUTE_NODE_AUTH_TOKEN'),
|
|
52
|
+
ip_address=os.environ.get('BIOLIB_COMPUTE_NODE_CUSTOM_IP', default=gethostbyname(gethostname())),
|
|
53
|
+
public_id=CloudUtils._get_environment_variable_or_fail('BIOLIB_COMPUTE_NODE_PUBLIC_ID'),
|
|
54
|
+
pybiolib_version=utils.BIOLIB_PACKAGE_VERSION,
|
|
55
|
+
),
|
|
56
|
+
base_url=CloudUtils._get_environment_variable_or_fail('BIOLIB_BASE_URL'),
|
|
57
|
+
is_dev=os.environ.get('BIOLIB_DEV') == 'TRUE',
|
|
58
|
+
shutdown_times=ShutdownTimes(
|
|
59
|
+
auto_shutdown_time_in_seconds=CloudUtils._get_environment_variable_as_int(
|
|
60
|
+
'BIOLIB_CLOUD_AUTO_SHUTDOWN_TIME_IN_SECONDS'
|
|
96
61
|
),
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
'BIOLIB_MAX_DOCKER_IMAGE_CACHE_SIZE_BYTES'
|
|
100
|
-
),
|
|
101
|
-
is_dev=CloudUtils._get_environment_variable('BIOLIB_DEV').upper() == 'TRUE',
|
|
102
|
-
shutdown_times=ShutdownTimes(
|
|
103
|
-
job_max_runtime_shutdown_time_in_seconds=CloudUtils._get_environment_variable_as_int(
|
|
104
|
-
'BIOLIB_CLOUD_JOB_MAX_RUNTIME_IN_SECONDS'
|
|
105
|
-
),
|
|
106
|
-
auto_shutdown_time_in_seconds=CloudUtils._get_environment_variable_as_int(
|
|
107
|
-
'BIOLIB_CLOUD_AUTO_SHUTDOWN_TIME_IN_SECONDS'
|
|
108
|
-
),
|
|
109
|
-
)
|
|
110
|
-
)
|
|
62
|
+
),
|
|
63
|
+
)
|
|
111
64
|
|
|
112
65
|
return CloudUtils._webserver_config
|
|
113
66
|
|
|
114
|
-
# Currently only used for enclaves
|
|
115
|
-
@staticmethod
|
|
116
|
-
def log(log_message: str, level: int) -> None:
|
|
117
|
-
CloudUtils.enclave.log_message_to_log_file(log_message, level)
|
|
118
|
-
|
|
119
67
|
@staticmethod
|
|
120
|
-
def
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# Sleep for 10 seconds to ensure logs are written
|
|
124
|
-
time.sleep(10)
|
|
125
|
-
|
|
126
|
-
if utils.BIOLIB_IS_RUNNING_IN_ENCLAVE:
|
|
127
|
-
CloudUtils.enclave.deregister_and_shutdown()
|
|
128
|
-
else:
|
|
68
|
+
def deregister(error: Optional[str] = None) -> None:
|
|
69
|
+
if utils.IS_RUNNING_IN_CLOUD:
|
|
129
70
|
config = CloudUtils.get_webserver_config()
|
|
130
71
|
try:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
72
|
+
api.client.post(
|
|
73
|
+
authenticate=False,
|
|
74
|
+
path='/jobs/deregister/',
|
|
75
|
+
data={
|
|
76
|
+
'auth_token': config['compute_node_info']['auth_token'],
|
|
77
|
+
'public_id': config['compute_node_info']['public_id'],
|
|
78
|
+
'error': error,
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
except BaseException as error_object:
|
|
82
|
+
logger_no_user_data.error(f'Failed to deregister got error: {error_object}')
|
|
83
|
+
else:
|
|
84
|
+
logger_no_user_data.error('Not deregistering as environment is not cloud')
|
|
137
85
|
|
|
138
|
-
|
|
86
|
+
@staticmethod
|
|
87
|
+
def shutdown() -> None:
|
|
88
|
+
if utils.IS_RUNNING_IN_CLOUD:
|
|
89
|
+
logger_no_user_data.debug('Waiting 10 seconds and shutting down...')
|
|
90
|
+
# Sleep for 10 seconds to ensure logs are written
|
|
91
|
+
time.sleep(10)
|
|
92
|
+
logger_no_user_data.debug('Shutting down...')
|
|
139
93
|
try:
|
|
140
94
|
subprocess.run(['sudo', 'shutdown', 'now'], check=True)
|
|
141
95
|
except Exception as error: # pylint: disable=broad-except
|
|
142
|
-
|
|
96
|
+
logger_no_user_data.error(f'Failed to shutdown got error: {error}')
|
|
97
|
+
else:
|
|
98
|
+
logger_no_user_data.error('Not running shutdown as environment is not cloud')
|
|
143
99
|
|
|
144
100
|
@staticmethod
|
|
145
|
-
def
|
|
146
|
-
|
|
147
|
-
try:
|
|
148
|
-
requests.post(
|
|
149
|
-
url=f'{config["base_url"]}/api/jobs/cloud/finish/',
|
|
150
|
-
json={
|
|
151
|
-
'auth_token': config["compute_node_info"]["auth_token"],
|
|
152
|
-
'job_id': job_id,
|
|
153
|
-
},
|
|
154
|
-
timeout=5
|
|
155
|
-
)
|
|
101
|
+
def deregister_and_shutdown() -> None:
|
|
102
|
+
logger_no_user_data.debug('Deregistering and shutting down...')
|
|
156
103
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
logger.error(error)
|
|
104
|
+
CloudUtils.deregister()
|
|
105
|
+
CloudUtils.shutdown()
|
|
160
106
|
|
|
161
107
|
@staticmethod
|
|
162
|
-
def
|
|
163
|
-
if not
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
pid = int(open(CloudUtils._auto_shutdown_timer_pid_file_name, 'r').read())
|
|
170
|
-
os.kill(pid, signal.SIGTERM)
|
|
171
|
-
|
|
172
|
-
except Exception as error: # pylint: disable=broad-except
|
|
173
|
-
logger.warning(error)
|
|
174
|
-
logger.warning('Could not kill old auto shutdown timer')
|
|
108
|
+
def finish_cloud_job(cloud_job_id: str, system_exception_code: Optional[int], exit_code: Optional[int]) -> None:
|
|
109
|
+
if not cloud_job_id:
|
|
110
|
+
logger_no_user_data.error(
|
|
111
|
+
'Finish cloud job was called but no cloud job was supplied. '
|
|
112
|
+
f'System exception code: {system_exception_code}'
|
|
113
|
+
)
|
|
114
|
+
return
|
|
175
115
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
116
|
+
logger_no_user_data.debug(
|
|
117
|
+
f'Reporting CloudJob "{cloud_job_id}" as finished with exit code: {exit_code} '
|
|
118
|
+
f'and system exception code: {system_exception_code}'
|
|
179
119
|
)
|
|
180
120
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
121
|
+
config = CloudUtils.get_webserver_config()
|
|
122
|
+
try:
|
|
123
|
+
api.client.post(
|
|
124
|
+
authenticate=False,
|
|
125
|
+
path='/jobs/cloud/finish/',
|
|
126
|
+
retries=100,
|
|
127
|
+
data={
|
|
128
|
+
'auth_token': config['compute_node_info']['auth_token'],
|
|
129
|
+
'cloud_job_id': cloud_job_id,
|
|
130
|
+
'system_exception_code': system_exception_code,
|
|
131
|
+
'exit_code': exit_code,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
except BaseException as error:
|
|
135
|
+
logger_no_user_data.debug(f'Failed to finish CloudJob "{cloud_job_id}" due to: {error}')
|
|
185
136
|
|
|
186
137
|
@staticmethod
|
|
187
138
|
def _report_availability() -> None:
|
|
@@ -189,35 +140,33 @@ class CloudUtils:
|
|
|
189
140
|
config = CloudUtils.get_webserver_config()
|
|
190
141
|
compute_node_info = config['compute_node_info']
|
|
191
142
|
api_client = BiolibApiClient.get()
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
break
|
|
201
|
-
except Exception as error: # pylint: disable=broad-except
|
|
202
|
-
logger.error(f'Self-registering failed with error: {error}')
|
|
203
|
-
if retry_count < max_retries - 1:
|
|
204
|
-
seconds_to_sleep = 1
|
|
205
|
-
logger.info(f'Retrying self-registering in {seconds_to_sleep} seconds')
|
|
206
|
-
time.sleep(seconds_to_sleep)
|
|
207
|
-
|
|
208
|
-
if not response:
|
|
209
|
-
raise BioLibError('Failed to register. Max retry limit reached')
|
|
210
|
-
|
|
143
|
+
logger_no_user_data.debug(
|
|
144
|
+
f'Registering with {compute_node_info} to host {api_client.base_url} at {datetime.now()}'
|
|
145
|
+
)
|
|
146
|
+
response = api.client.post(
|
|
147
|
+
authenticate=False,
|
|
148
|
+
path='/jobs/report_available/',
|
|
149
|
+
data=cast(Dict[str, str], compute_node_info),
|
|
150
|
+
)
|
|
211
151
|
if response.status_code != 201:
|
|
212
|
-
raise Exception(
|
|
152
|
+
raise Exception('Non 201 error code')
|
|
153
|
+
else:
|
|
154
|
+
logger_no_user_data.info('Compute node registered!')
|
|
155
|
+
response_data = response.json()
|
|
156
|
+
logger_no_user_data.info(f'Got data on register: {json.dumps(response_data)}')
|
|
157
|
+
certs = []
|
|
158
|
+
for federation in response_data['federation']:
|
|
159
|
+
for cert_b64 in federation['certs_b64']:
|
|
160
|
+
certs.append(base64.b64decode(cert_b64).decode())
|
|
161
|
+
trust_ceritificates(certs)
|
|
213
162
|
|
|
214
163
|
except Exception as exception: # pylint: disable=broad-except
|
|
215
|
-
|
|
164
|
+
logger_no_user_data.error(f'Shutting down as self register failed due to: {exception}')
|
|
216
165
|
if not utils.IS_DEV:
|
|
217
166
|
CloudUtils.deregister_and_shutdown()
|
|
218
167
|
|
|
219
168
|
@staticmethod
|
|
220
|
-
def
|
|
169
|
+
def _get_environment_variable_or_fail(key: str) -> str:
|
|
221
170
|
value = os.environ.get(key)
|
|
222
171
|
# Purposely loose falsy check (instead of `is not None`) as empty string should fail
|
|
223
172
|
if not value:
|
|
@@ -227,4 +176,4 @@ class CloudUtils:
|
|
|
227
176
|
|
|
228
177
|
@staticmethod
|
|
229
178
|
def _get_environment_variable_as_int(key: str) -> int:
|
|
230
|
-
return int(CloudUtils.
|
|
179
|
+
return int(CloudUtils._get_environment_variable_or_fail(key))
|
|
@@ -1,104 +1,60 @@
|
|
|
1
|
-
import abc
|
|
2
1
|
import json
|
|
3
2
|
import os
|
|
4
3
|
import shutil
|
|
5
|
-
import time
|
|
6
4
|
import uuid
|
|
7
|
-
from datetime import datetime
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from biolib.biolib_errors import BioLibError
|
|
6
|
+
from biolib.biolib_logging import logger_no_user_data
|
|
12
7
|
from biolib.compute_node.job_worker.cache_types import LfsCacheStateDict, UuidStr, StoragePartition, \
|
|
13
8
|
DockerImageCacheStateDict
|
|
14
|
-
from biolib.typing_utils import Dict, List,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class CacheStateError(BioLibError):
|
|
18
|
-
pass
|
|
9
|
+
from biolib.typing_utils import Dict, List, cast, Optional
|
|
10
|
+
from biolib.utils.cache_state import CacheState, CacheStateError
|
|
19
11
|
|
|
20
12
|
|
|
21
13
|
class DockerCacheStateError(CacheStateError):
|
|
22
14
|
pass
|
|
23
15
|
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
class LfsCacheState(CacheState):
|
|
26
18
|
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
super().__init__()
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
self._storage_path_for_write: str = self._get_storage_path_for_write()
|
|
23
|
+
self._storage_paths: List[str] = self._get_storage_paths()
|
|
24
|
+
self._tmp_storage_paths: List[str] = self._get_tmp_storage_paths()
|
|
30
25
|
|
|
31
26
|
@property
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
raise NotImplementedError
|
|
27
|
+
def storage_paths(self) -> List[str]:
|
|
28
|
+
return self._storage_paths
|
|
35
29
|
|
|
36
30
|
@property
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
raise NotImplementedError
|
|
40
|
-
|
|
41
|
-
@abc.abstractmethod
|
|
42
|
-
def _get_default_state(self) -> StateType:
|
|
43
|
-
raise NotImplementedError
|
|
44
|
-
|
|
45
|
-
def __init__(self):
|
|
46
|
-
self._state: Optional[StateType] = None
|
|
31
|
+
def storage_path_for_write(self) -> str:
|
|
32
|
+
return self._storage_path_for_write
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
with open(self._state_path, mode='r') as file:
|
|
52
|
-
self._state = json.loads(file.read())
|
|
53
|
-
else:
|
|
54
|
-
self._state = self._get_default_state()
|
|
55
|
-
with open(self._state_path, mode='w') as file:
|
|
56
|
-
file.write(json.dumps(self._state))
|
|
57
|
-
|
|
58
|
-
# Check for type checking
|
|
59
|
-
if self._state is None:
|
|
60
|
-
raise CacheStateError('Internal state is not defined')
|
|
61
|
-
|
|
62
|
-
return self._state
|
|
63
|
-
|
|
64
|
-
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
65
|
-
with open(self._state_path, mode='w') as file:
|
|
66
|
-
file.write(json.dumps(self._state))
|
|
67
|
-
|
|
68
|
-
self._release_state_lock()
|
|
69
|
-
|
|
70
|
-
def _acquire_state_lock(self) -> None:
|
|
71
|
-
timeout_seconds = 5.0
|
|
72
|
-
seconds_to_sleep = 0.5
|
|
73
|
-
while os.path.exists(self._state_lock_path):
|
|
74
|
-
time.sleep(seconds_to_sleep)
|
|
75
|
-
timeout_seconds -= seconds_to_sleep
|
|
76
|
-
if timeout_seconds < 0:
|
|
77
|
-
raise CacheStateError('Cache state timed out waiting for lock file')
|
|
78
|
-
|
|
79
|
-
os.makedirs(self._cache_dir, exist_ok=True)
|
|
80
|
-
lock_file = open(self._state_lock_path, mode='x')
|
|
81
|
-
lock_file.close()
|
|
82
|
-
|
|
83
|
-
def _release_state_lock(self) -> None:
|
|
84
|
-
if os.path.exists(self._state_lock_path):
|
|
85
|
-
os.remove(self._state_lock_path)
|
|
86
|
-
else:
|
|
87
|
-
raise CacheStateError('Cache state was not locked.')
|
|
88
|
-
|
|
89
|
-
@staticmethod
|
|
90
|
-
def get_timestamp_now() -> str:
|
|
91
|
-
return datetime.now().isoformat()
|
|
34
|
+
@property
|
|
35
|
+
def tmp_storage_paths(self) -> List[str]:
|
|
36
|
+
return self._tmp_storage_paths
|
|
92
37
|
|
|
38
|
+
@property
|
|
39
|
+
def main_lfs_storage_path(self) -> str:
|
|
40
|
+
return self._storage_paths[0]
|
|
93
41
|
|
|
94
|
-
class LfsCacheState(CacheState):
|
|
95
42
|
@property
|
|
96
43
|
def _state_path(self) -> str:
|
|
97
|
-
|
|
44
|
+
state_path = os.environ.get('BIOLIB_LFS_STATE_PATH')
|
|
45
|
+
if not state_path:
|
|
46
|
+
raise CacheStateError('Environment variable "BIOLIB_LFS_STATE_PATH" not set')
|
|
98
47
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
48
|
+
return state_path
|
|
49
|
+
|
|
50
|
+
def get_read_only_dict_without_lock(self) -> Optional[LfsCacheStateDict]:
|
|
51
|
+
try:
|
|
52
|
+
if not os.path.isfile(self._state_path):
|
|
53
|
+
return None
|
|
54
|
+
with open(self._state_path, mode='r') as file:
|
|
55
|
+
return cast(LfsCacheStateDict, json.load(file))
|
|
56
|
+
except BaseException:
|
|
57
|
+
return None
|
|
102
58
|
|
|
103
59
|
def _get_default_state(self) -> LfsCacheStateDict:
|
|
104
60
|
return LfsCacheStateDict(
|
|
@@ -107,26 +63,52 @@ class LfsCacheState(CacheState):
|
|
|
107
63
|
)
|
|
108
64
|
|
|
109
65
|
@staticmethod
|
|
110
|
-
def
|
|
66
|
+
def _get_tmp_storage_paths() -> List[str]:
|
|
111
67
|
lfs_tmp_storage_path_env_key = 'BIOLIB_LFS_TMP_STORAGE_PATHS'
|
|
112
68
|
lfs_tmp_storage_paths = os.environ.get(lfs_tmp_storage_path_env_key)
|
|
113
|
-
if lfs_tmp_storage_paths
|
|
69
|
+
if not lfs_tmp_storage_paths:
|
|
114
70
|
raise CacheStateError(f'Environment variable "{lfs_tmp_storage_path_env_key}" not set')
|
|
115
71
|
|
|
116
|
-
|
|
72
|
+
lfs_tmp_storage_paths_list = lfs_tmp_storage_paths.split(',')
|
|
73
|
+
for lfs_tmp_storage_path in lfs_tmp_storage_paths_list:
|
|
74
|
+
if not os.path.isdir(lfs_tmp_storage_path):
|
|
75
|
+
raise CacheStateError(f'LFS temporary storage path {lfs_tmp_storage_path} is not a directory')
|
|
76
|
+
|
|
77
|
+
return lfs_tmp_storage_paths_list
|
|
117
78
|
|
|
118
79
|
@staticmethod
|
|
119
|
-
def
|
|
80
|
+
def _get_storage_paths() -> List[str]:
|
|
120
81
|
lfs_storage_path_env_key = 'BIOLIB_LFS_STORAGE_PATHS'
|
|
121
82
|
lfs_storage_paths = os.environ.get(lfs_storage_path_env_key)
|
|
122
|
-
|
|
83
|
+
logger_no_user_data.debug(f'LFS storage paths: {lfs_storage_paths}')
|
|
84
|
+
|
|
85
|
+
# It is essential to check like this so we catch if it is None and if it is empty string
|
|
86
|
+
if not lfs_storage_paths:
|
|
123
87
|
raise CacheStateError(f'Environment variable "{lfs_storage_path_env_key}" not set')
|
|
124
88
|
|
|
125
|
-
|
|
126
|
-
for lfs_storage_path in
|
|
89
|
+
lfs_storage_paths_list = lfs_storage_paths.split(',')
|
|
90
|
+
for lfs_storage_path in lfs_storage_paths_list:
|
|
127
91
|
if not os.path.isdir(lfs_storage_path):
|
|
128
92
|
raise CacheStateError(f'LFS storage path {lfs_storage_path} is not a directory')
|
|
129
93
|
|
|
94
|
+
return lfs_storage_paths_list
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _get_storage_path_for_write() -> str:
|
|
98
|
+
lfs_storage_path_for_write = os.environ.get('BIOLIB_LFS_WRITE_STORAGE_PATH')
|
|
99
|
+
logger_no_user_data.debug(f'lfs_storage_path_for_write={lfs_storage_path_for_write}')
|
|
100
|
+
|
|
101
|
+
if not lfs_storage_path_for_write:
|
|
102
|
+
raise CacheStateError('Environment variable "BIOLIB_LFS_WRITE_STORAGE_PATH" not set')
|
|
103
|
+
|
|
104
|
+
if not os.path.isdir(lfs_storage_path_for_write):
|
|
105
|
+
raise CacheStateError(f'LFS storage path {lfs_storage_path_for_write} is not a directory')
|
|
106
|
+
|
|
107
|
+
return lfs_storage_path_for_write
|
|
108
|
+
|
|
109
|
+
def _get_storage_partitions_from_env(self) -> Dict[UuidStr, StoragePartition]:
|
|
110
|
+
storage_states: Dict[UuidStr, StoragePartition] = {}
|
|
111
|
+
for lfs_storage_path in self._storage_paths:
|
|
130
112
|
uuid_str = str(uuid.uuid4())
|
|
131
113
|
disk_usage = shutil.disk_usage(lfs_storage_path)
|
|
132
114
|
storage_states[uuid_str] = StoragePartition(
|
|
@@ -142,11 +124,7 @@ class LfsCacheState(CacheState):
|
|
|
142
124
|
class DockerImageCacheState(CacheState):
|
|
143
125
|
@property
|
|
144
126
|
def _state_path(self) -> str:
|
|
145
|
-
return f'{
|
|
146
|
-
|
|
147
|
-
@property
|
|
148
|
-
def _state_lock_path(self) -> str:
|
|
149
|
-
return f'{self._state_path}.lock'
|
|
127
|
+
return f'{self._user_cache_dir}/docker-cache-state.json'
|
|
150
128
|
|
|
151
129
|
def _get_default_state(self) -> DockerImageCacheStateDict:
|
|
152
130
|
return {}
|
|
@@ -6,7 +6,6 @@ DockerImageUri = str
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class LargeFileSystemCache(TypedDict):
|
|
9
|
-
active_jobs: List[UuidStr]
|
|
10
9
|
last_used_at: str
|
|
11
10
|
size_bytes: int
|
|
12
11
|
state: Literal['downloading', 'ready']
|
|
@@ -26,15 +25,11 @@ class LfsCacheStateDict(TypedDict):
|
|
|
26
25
|
large_file_systems: Dict[UuidStr, LargeFileSystemCache]
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
class DockerAuthConfig(TypedDict):
|
|
30
|
-
username: str
|
|
31
|
-
password: str
|
|
32
|
-
|
|
33
|
-
|
|
34
28
|
class DockerImageInfo(TypedDict):
|
|
35
29
|
last_used_at: str
|
|
36
30
|
estimated_image_size_bytes: int
|
|
37
31
|
state: Literal['pulling', 'ready']
|
|
32
|
+
active_jobs: List[UuidStr]
|
|
38
33
|
uri: str
|
|
39
34
|
|
|
40
35
|
|