loopix-sdk 2.30.0__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.
- loopix/__init__.py +260 -0
- loopix/api/__init__.py +287 -0
- loopix/api/client/__init__.py +8 -0
- loopix/api/client/api/__init__.py +1 -0
- loopix/api/client/api/sandboxes/__init__.py +1 -0
- loopix/api/client/api/sandboxes/delete_sandboxes_sandbox_id.py +161 -0
- loopix/api/client/api/sandboxes/get_sandboxes.py +176 -0
- loopix/api/client/api/sandboxes/get_sandboxes_metrics.py +173 -0
- loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id.py +163 -0
- loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id_logs.py +199 -0
- loopix/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +212 -0
- loopix/api/client/api/sandboxes/get_v2_sandboxes.py +230 -0
- loopix/api/client/api/sandboxes/get_v_2_sandboxes_sandbox_id_logs.py +254 -0
- loopix/api/client/api/sandboxes/post_sandboxes.py +172 -0
- loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
- loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_pause.py +187 -0
- loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_refreshes.py +181 -0
- loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_resume.py +189 -0
- loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_snapshots.py +195 -0
- loopix/api/client/api/sandboxes/post_sandboxes_sandbox_id_timeout.py +193 -0
- loopix/api/client/api/sandboxes/put_sandboxes_sandbox_id_network.py +199 -0
- loopix/api/client/api/snapshots/__init__.py +1 -0
- loopix/api/client/api/snapshots/get_snapshots.py +202 -0
- loopix/api/client/api/tags/__init__.py +1 -0
- loopix/api/client/api/tags/delete_templates_tags.py +174 -0
- loopix/api/client/api/tags/get_templates_template_id_tags.py +172 -0
- loopix/api/client/api/tags/post_templates_tags.py +176 -0
- loopix/api/client/api/templates/__init__.py +1 -0
- loopix/api/client/api/templates/delete_templates_template_id.py +157 -0
- loopix/api/client/api/templates/get_templates.py +172 -0
- loopix/api/client/api/templates/get_templates_aliases_alias.py +167 -0
- loopix/api/client/api/templates/get_templates_template_id.py +195 -0
- loopix/api/client/api/templates/get_templates_template_id_builds_build_id_logs.py +272 -0
- loopix/api/client/api/templates/get_templates_template_id_builds_build_id_status.py +232 -0
- loopix/api/client/api/templates/get_templates_template_id_files_hash.py +180 -0
- loopix/api/client/api/templates/patch_templates_template_id.py +183 -0
- loopix/api/client/api/templates/patch_v_2_templates_template_id.py +185 -0
- loopix/api/client/api/templates/post_templates.py +172 -0
- loopix/api/client/api/templates/post_templates_template_id.py +181 -0
- loopix/api/client/api/templates/post_templates_template_id_builds_build_id.py +170 -0
- loopix/api/client/api/templates/post_v2_templates.py +172 -0
- loopix/api/client/api/templates/post_v3_templates.py +176 -0
- loopix/api/client/api/templates/post_v_2_templates_template_id_builds_build_id.py +192 -0
- loopix/api/client/api/volumes/__init__.py +1 -0
- loopix/api/client/api/volumes/delete_volumes_volume_id.py +161 -0
- loopix/api/client/api/volumes/get_volumes.py +140 -0
- loopix/api/client/api/volumes/get_volumes_volume_id.py +163 -0
- loopix/api/client/api/volumes/post_volumes.py +172 -0
- loopix/api/client/client.py +286 -0
- loopix/api/client/errors.py +16 -0
- loopix/api/client/models/__init__.py +185 -0
- loopix/api/client/models/admin_build_cancel_result.py +67 -0
- loopix/api/client/models/admin_sandbox_kill_result.py +67 -0
- loopix/api/client/models/assign_template_tags_request.py +67 -0
- loopix/api/client/models/assigned_template_tags.py +68 -0
- loopix/api/client/models/aws_registry.py +85 -0
- loopix/api/client/models/aws_registry_type.py +8 -0
- loopix/api/client/models/build_log_entry.py +89 -0
- loopix/api/client/models/build_status_reason.py +95 -0
- loopix/api/client/models/connect_sandbox.py +59 -0
- loopix/api/client/models/created_access_token.py +100 -0
- loopix/api/client/models/created_team_api_key.py +166 -0
- loopix/api/client/models/delete_template_tags_request.py +67 -0
- loopix/api/client/models/disk_metrics.py +91 -0
- loopix/api/client/models/error.py +67 -0
- loopix/api/client/models/gcp_registry.py +69 -0
- loopix/api/client/models/gcp_registry_type.py +8 -0
- loopix/api/client/models/general_registry.py +77 -0
- loopix/api/client/models/general_registry_type.py +8 -0
- loopix/api/client/models/identifier_masking_details.py +83 -0
- loopix/api/client/models/listed_sandbox.py +179 -0
- loopix/api/client/models/log_level.py +11 -0
- loopix/api/client/models/logs_direction.py +9 -0
- loopix/api/client/models/logs_source.py +9 -0
- loopix/api/client/models/machine_info.py +83 -0
- loopix/api/client/models/max_team_metric.py +78 -0
- loopix/api/client/models/mcp_type_0.py +44 -0
- loopix/api/client/models/new_access_token.py +59 -0
- loopix/api/client/models/new_sandbox.py +224 -0
- loopix/api/client/models/new_team_api_key.py +59 -0
- loopix/api/client/models/new_volume.py +59 -0
- loopix/api/client/models/node.py +160 -0
- loopix/api/client/models/node_detail.py +160 -0
- loopix/api/client/models/node_metrics.py +122 -0
- loopix/api/client/models/node_status.py +12 -0
- loopix/api/client/models/node_status_change.py +82 -0
- loopix/api/client/models/post_sandboxes_sandbox_id_refreshes_body.py +59 -0
- loopix/api/client/models/post_sandboxes_sandbox_id_snapshots_body.py +60 -0
- loopix/api/client/models/post_sandboxes_sandbox_id_timeout_body.py +59 -0
- loopix/api/client/models/resumed_sandbox.py +68 -0
- loopix/api/client/models/sandbox.py +145 -0
- loopix/api/client/models/sandbox_auto_resume_config.py +60 -0
- loopix/api/client/models/sandbox_detail.py +267 -0
- loopix/api/client/models/sandbox_lifecycle.py +70 -0
- loopix/api/client/models/sandbox_log.py +70 -0
- loopix/api/client/models/sandbox_log_entry.py +93 -0
- loopix/api/client/models/sandbox_log_entry_fields.py +44 -0
- loopix/api/client/models/sandbox_logs.py +91 -0
- loopix/api/client/models/sandbox_logs_v2_response.py +73 -0
- loopix/api/client/models/sandbox_metric.py +126 -0
- loopix/api/client/models/sandbox_network_config.py +118 -0
- loopix/api/client/models/sandbox_network_config_rules.py +72 -0
- loopix/api/client/models/sandbox_network_rule.py +74 -0
- loopix/api/client/models/sandbox_network_transform.py +79 -0
- loopix/api/client/models/sandbox_network_transform_headers.py +47 -0
- loopix/api/client/models/sandbox_network_update_config.py +114 -0
- loopix/api/client/models/sandbox_network_update_config_rules.py +71 -0
- loopix/api/client/models/sandbox_on_timeout.py +9 -0
- loopix/api/client/models/sandbox_pause_request.py +62 -0
- loopix/api/client/models/sandbox_state.py +9 -0
- loopix/api/client/models/sandbox_volume_mount.py +67 -0
- loopix/api/client/models/sandboxes_with_metrics.py +59 -0
- loopix/api/client/models/snapshot_info.py +70 -0
- loopix/api/client/models/team.py +83 -0
- loopix/api/client/models/team_api_key.py +158 -0
- loopix/api/client/models/team_metric.py +86 -0
- loopix/api/client/models/team_user.py +75 -0
- loopix/api/client/models/template.py +225 -0
- loopix/api/client/models/template_alias_response.py +67 -0
- loopix/api/client/models/template_build.py +139 -0
- loopix/api/client/models/template_build_file_upload.py +70 -0
- loopix/api/client/models/template_build_info.py +126 -0
- loopix/api/client/models/template_build_logs_response.py +73 -0
- loopix/api/client/models/template_build_request.py +115 -0
- loopix/api/client/models/template_build_request_v2.py +88 -0
- loopix/api/client/models/template_build_request_v3.py +107 -0
- loopix/api/client/models/template_build_start_v2.py +184 -0
- loopix/api/client/models/template_build_status.py +11 -0
- loopix/api/client/models/template_legacy.py +207 -0
- loopix/api/client/models/template_request_response_v3.py +99 -0
- loopix/api/client/models/template_step.py +91 -0
- loopix/api/client/models/template_tag.py +78 -0
- loopix/api/client/models/template_update_request.py +59 -0
- loopix/api/client/models/template_update_response.py +59 -0
- loopix/api/client/models/template_with_builds.py +156 -0
- loopix/api/client/models/update_team_api_key.py +59 -0
- loopix/api/client/models/volume.py +67 -0
- loopix/api/client/models/volume_and_token.py +75 -0
- loopix/api/client/models/volume_token.py +59 -0
- loopix/api/client/py.typed +1 -0
- loopix/api/client/types.py +54 -0
- loopix/api/client_async/__init__.py +74 -0
- loopix/api/client_sync/__init__.py +73 -0
- loopix/api/metadata.py +14 -0
- loopix/connection_config.py +309 -0
- loopix/envd/api.py +170 -0
- loopix/envd/filesystem/filesystem_connect.py +193 -0
- loopix/envd/filesystem/filesystem_pb2.py +80 -0
- loopix/envd/filesystem/filesystem_pb2.pyi +272 -0
- loopix/envd/process/process_connect.py +174 -0
- loopix/envd/process/process_pb2.py +96 -0
- loopix/envd/process/process_pb2.pyi +316 -0
- loopix/envd/rpc.py +139 -0
- loopix/envd/versions.py +11 -0
- loopix/exceptions.py +133 -0
- loopix/io_utils.py +57 -0
- loopix/paginator.py +52 -0
- loopix/py.typed +0 -0
- loopix/sandbox/_git/__init__.py +85 -0
- loopix/sandbox/_git/args.py +363 -0
- loopix/sandbox/_git/auth.py +132 -0
- loopix/sandbox/_git/config.py +32 -0
- loopix/sandbox/_git/parse.py +222 -0
- loopix/sandbox/_git/types.py +149 -0
- loopix/sandbox/commands/command_handle.py +69 -0
- loopix/sandbox/commands/main.py +39 -0
- loopix/sandbox/filesystem/filesystem.py +337 -0
- loopix/sandbox/filesystem/watch_handle.py +70 -0
- loopix/sandbox/main.py +227 -0
- loopix/sandbox/mcp.py +1949 -0
- loopix/sandbox/network.py +8 -0
- loopix/sandbox/sandbox_api.py +624 -0
- loopix/sandbox/signature.py +47 -0
- loopix/sandbox/utils.py +34 -0
- loopix/sandbox_async/commands/command.py +396 -0
- loopix/sandbox_async/commands/command_handle.py +298 -0
- loopix/sandbox_async/commands/pty.py +257 -0
- loopix/sandbox_async/filesystem/filesystem.py +720 -0
- loopix/sandbox_async/filesystem/watch_handle.py +97 -0
- loopix/sandbox_async/git.py +1100 -0
- loopix/sandbox_async/main.py +987 -0
- loopix/sandbox_async/paginator.py +140 -0
- loopix/sandbox_async/sandbox_api.py +504 -0
- loopix/sandbox_async/utils.py +7 -0
- loopix/sandbox_domains.py +5 -0
- loopix/sandbox_sync/commands/command.py +420 -0
- loopix/sandbox_sync/commands/command_handle.py +239 -0
- loopix/sandbox_sync/commands/pty.py +279 -0
- loopix/sandbox_sync/filesystem/filesystem.py +710 -0
- loopix/sandbox_sync/filesystem/watch_handle.py +102 -0
- loopix/sandbox_sync/git.py +1077 -0
- loopix/sandbox_sync/main.py +975 -0
- loopix/sandbox_sync/paginator.py +140 -0
- loopix/sandbox_sync/sandbox_api.py +491 -0
- loopix/template/consts.py +45 -0
- loopix/template/dockerfile_parser.py +286 -0
- loopix/template/logger.py +232 -0
- loopix/template/main.py +1368 -0
- loopix/template/readycmd.py +144 -0
- loopix/template/types.py +194 -0
- loopix/template/utils.py +426 -0
- loopix/template_async/build_api.py +419 -0
- loopix/template_async/main.py +528 -0
- loopix/template_sync/build_api.py +409 -0
- loopix/template_sync/main.py +529 -0
- loopix/volume/client/__init__.py +8 -0
- loopix/volume/client/api/__init__.py +1 -0
- loopix/volume/client/api/volumes/__init__.py +1 -0
- loopix/volume/client/api/volumes/delete_volumecontent_volume_id_path.py +174 -0
- loopix/volume/client/api/volumes/get_volumecontent_volume_id_dir.py +204 -0
- loopix/volume/client/api/volumes/get_volumecontent_volume_id_file.py +179 -0
- loopix/volume/client/api/volumes/get_volumecontent_volume_id_path.py +176 -0
- loopix/volume/client/api/volumes/patch_volumecontent_volume_id_path.py +203 -0
- loopix/volume/client/api/volumes/post_volumecontent_volume_id_dir.py +239 -0
- loopix/volume/client/api/volumes/put_volumecontent_volume_id_file.py +259 -0
- loopix/volume/client/client.py +286 -0
- loopix/volume/client/errors.py +16 -0
- loopix/volume/client/models/__init__.py +13 -0
- loopix/volume/client/models/error.py +67 -0
- loopix/volume/client/models/patch_volumecontent_volume_id_path_body.py +77 -0
- loopix/volume/client/models/volume_entry_stat.py +145 -0
- loopix/volume/client/models/volume_entry_stat_type.py +11 -0
- loopix/volume/client/py.typed +1 -0
- loopix/volume/client/types.py +54 -0
- loopix/volume/client_async/__init__.py +88 -0
- loopix/volume/client_sync/__init__.py +80 -0
- loopix/volume/connection_config.py +145 -0
- loopix/volume/types.py +62 -0
- loopix/volume/utils.py +52 -0
- loopix/volume/volume_async.py +639 -0
- loopix/volume/volume_sync.py +639 -0
- loopix_connect/__init__.py +1 -0
- loopix_connect/client.py +534 -0
- loopix_connect/py.typed +0 -0
- loopix_sdk-2.30.0.dist-info/METADATA +98 -0
- loopix_sdk-2.30.0.dist-info/RECORD +238 -0
- loopix_sdk-2.30.0.dist-info/WHEEL +4 -0
- loopix_sdk-2.30.0.dist-info/licenses/LICENSE +9 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Callable, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from typing_extensions import Unpack
|
|
5
|
+
|
|
6
|
+
from loopix.api.client.client import AuthenticatedClient
|
|
7
|
+
from loopix.connection_config import ApiParams, ConnectionConfig
|
|
8
|
+
from loopix.template.consts import GZIP, RESOLVE_SYMLINKS
|
|
9
|
+
from loopix.template.logger import LogEntry, LogEntryEnd, LogEntryStart
|
|
10
|
+
from loopix.template.main import TemplateBase, TemplateClass
|
|
11
|
+
from loopix.template.types import BuildInfo, InstructionType, TemplateTag, TemplateTagInfo
|
|
12
|
+
from loopix.template.utils import normalize_build_arguments, read_dockerignore
|
|
13
|
+
|
|
14
|
+
from .build_api import (
|
|
15
|
+
assign_tags,
|
|
16
|
+
check_alias_exists,
|
|
17
|
+
get_template_tags,
|
|
18
|
+
remove_tags,
|
|
19
|
+
get_build_status,
|
|
20
|
+
get_file_upload_link,
|
|
21
|
+
request_build,
|
|
22
|
+
trigger_build,
|
|
23
|
+
upload_file,
|
|
24
|
+
wait_for_build_finish,
|
|
25
|
+
)
|
|
26
|
+
from loopix.api.client_async import get_api_client
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AsyncTemplate(TemplateBase):
|
|
30
|
+
"""
|
|
31
|
+
Asynchronous template builder for Loopix sandboxes.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
async def _build(
|
|
36
|
+
api_client: AuthenticatedClient,
|
|
37
|
+
template: TemplateClass,
|
|
38
|
+
name: str,
|
|
39
|
+
tags: Optional[List[str]] = None,
|
|
40
|
+
cpu_count: int = 2,
|
|
41
|
+
memory_mb: int = 1024,
|
|
42
|
+
skip_cache: bool = False,
|
|
43
|
+
on_build_logs: Optional[Callable[[LogEntry], None]] = None,
|
|
44
|
+
request_timeout: Optional[float] = None,
|
|
45
|
+
) -> BuildInfo:
|
|
46
|
+
"""
|
|
47
|
+
Internal implementation of the template build process
|
|
48
|
+
|
|
49
|
+
:param api_client: Authenticated API client
|
|
50
|
+
:param template: The template to build
|
|
51
|
+
:param name: Name for the template
|
|
52
|
+
:param tags: Optional tags for the template
|
|
53
|
+
:param cpu_count: Number of CPUs allocated to the sandbox
|
|
54
|
+
:param memory_mb: Amount of memory in MB allocated to the sandbox
|
|
55
|
+
:param skip_cache: If True, forces a complete rebuild ignoring cache
|
|
56
|
+
:param on_build_logs: Callback function to receive build logs during the build process
|
|
57
|
+
"""
|
|
58
|
+
if skip_cache:
|
|
59
|
+
template._template._force = True
|
|
60
|
+
|
|
61
|
+
# Create template
|
|
62
|
+
if on_build_logs:
|
|
63
|
+
tags_msg = f" with tags: {', '.join(tags)}" if tags else ""
|
|
64
|
+
on_build_logs(
|
|
65
|
+
LogEntry(
|
|
66
|
+
timestamp=datetime.now(),
|
|
67
|
+
level="info",
|
|
68
|
+
message=f"Requesting build for template: {name}{tags_msg}",
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
response = await request_build(
|
|
73
|
+
api_client,
|
|
74
|
+
name=name,
|
|
75
|
+
cpu_count=cpu_count,
|
|
76
|
+
memory_mb=memory_mb,
|
|
77
|
+
tags=tags,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
template_id = response.template_id
|
|
81
|
+
build_id = response.build_id
|
|
82
|
+
response_tags = response.tags
|
|
83
|
+
|
|
84
|
+
if on_build_logs:
|
|
85
|
+
on_build_logs(
|
|
86
|
+
LogEntry(
|
|
87
|
+
timestamp=datetime.now(),
|
|
88
|
+
level="info",
|
|
89
|
+
message=f"Template created with ID: {template_id}, Build ID: {build_id}",
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
instructions_with_hashes = template._template._instructions_with_hashes()
|
|
94
|
+
|
|
95
|
+
# Upload files
|
|
96
|
+
for index, file_upload in enumerate(instructions_with_hashes):
|
|
97
|
+
if file_upload["type"] != InstructionType.COPY:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
args = file_upload.get("args", [])
|
|
101
|
+
src = args[0] if len(args) > 0 else None
|
|
102
|
+
force_upload = file_upload.get("forceUpload")
|
|
103
|
+
files_hash = file_upload.get("filesHash", None)
|
|
104
|
+
resolve_symlinks = file_upload.get("resolveSymlinks")
|
|
105
|
+
if resolve_symlinks is None:
|
|
106
|
+
resolve_symlinks = RESOLVE_SYMLINKS
|
|
107
|
+
gzip = file_upload.get("gzip")
|
|
108
|
+
if gzip is None:
|
|
109
|
+
gzip = GZIP
|
|
110
|
+
|
|
111
|
+
if src is None or files_hash is None:
|
|
112
|
+
raise ValueError("Source path and files hash are required")
|
|
113
|
+
|
|
114
|
+
stack_trace = None
|
|
115
|
+
if index + 1 < len(template._template._stack_traces):
|
|
116
|
+
stack_trace = template._template._stack_traces[index + 1]
|
|
117
|
+
|
|
118
|
+
file_info = await get_file_upload_link(
|
|
119
|
+
api_client, template_id, files_hash, stack_trace
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if (force_upload and file_info.url) or (
|
|
123
|
+
file_info.present is False and file_info.url
|
|
124
|
+
):
|
|
125
|
+
await upload_file(
|
|
126
|
+
api_client,
|
|
127
|
+
src,
|
|
128
|
+
template._template._file_context_path,
|
|
129
|
+
file_info.url,
|
|
130
|
+
[
|
|
131
|
+
*template._template._file_ignore_patterns,
|
|
132
|
+
*read_dockerignore(template._template._file_context_path),
|
|
133
|
+
],
|
|
134
|
+
resolve_symlinks,
|
|
135
|
+
gzip,
|
|
136
|
+
stack_trace,
|
|
137
|
+
request_timeout=request_timeout,
|
|
138
|
+
)
|
|
139
|
+
if on_build_logs:
|
|
140
|
+
on_build_logs(
|
|
141
|
+
LogEntry(
|
|
142
|
+
timestamp=datetime.now(),
|
|
143
|
+
level="info",
|
|
144
|
+
message=f"Uploaded '{src}'",
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
if on_build_logs:
|
|
149
|
+
on_build_logs(
|
|
150
|
+
LogEntry(
|
|
151
|
+
timestamp=datetime.now(),
|
|
152
|
+
level="info",
|
|
153
|
+
message=f"Skipping upload of '{src}', already cached",
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if on_build_logs:
|
|
158
|
+
on_build_logs(
|
|
159
|
+
LogEntry(
|
|
160
|
+
timestamp=datetime.now(),
|
|
161
|
+
level="info",
|
|
162
|
+
message="All file uploads completed",
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Start build
|
|
167
|
+
if on_build_logs:
|
|
168
|
+
on_build_logs(
|
|
169
|
+
LogEntry(
|
|
170
|
+
timestamp=datetime.now(),
|
|
171
|
+
level="info",
|
|
172
|
+
message="Starting building...",
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
await trigger_build(
|
|
177
|
+
api_client,
|
|
178
|
+
template_id,
|
|
179
|
+
build_id,
|
|
180
|
+
template._template._serialize(instructions_with_hashes),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
return BuildInfo(
|
|
184
|
+
template_id=template_id,
|
|
185
|
+
build_id=build_id,
|
|
186
|
+
alias=name,
|
|
187
|
+
name=name,
|
|
188
|
+
tags=response_tags,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
async def build(
|
|
193
|
+
template: TemplateClass,
|
|
194
|
+
name: Optional[str] = None,
|
|
195
|
+
*,
|
|
196
|
+
alias: Optional[str] = None,
|
|
197
|
+
tags: Optional[List[str]] = None,
|
|
198
|
+
cpu_count: int = 2,
|
|
199
|
+
memory_mb: int = 1024,
|
|
200
|
+
skip_cache: bool = False,
|
|
201
|
+
on_build_logs: Optional[Callable[[LogEntry], None]] = None,
|
|
202
|
+
**opts: Unpack[ApiParams],
|
|
203
|
+
) -> BuildInfo:
|
|
204
|
+
"""
|
|
205
|
+
Build and deploy a template to Loopix infrastructure.
|
|
206
|
+
|
|
207
|
+
:param template: The template to build
|
|
208
|
+
:param name: Template name in 'name' or 'name:tag' format
|
|
209
|
+
:param alias: (Deprecated) Alias name for the template. Use name instead.
|
|
210
|
+
:param tags: Optional additional tags to assign to the template
|
|
211
|
+
:param cpu_count: Number of CPUs allocated to the sandbox
|
|
212
|
+
:param memory_mb: Amount of memory in MB allocated to the sandbox
|
|
213
|
+
:param skip_cache: If True, forces a complete rebuild ignoring cache
|
|
214
|
+
:param on_build_logs: Callback function to receive build logs during the build process
|
|
215
|
+
|
|
216
|
+
Example
|
|
217
|
+
```python
|
|
218
|
+
from loopix import AsyncTemplate
|
|
219
|
+
|
|
220
|
+
template = (
|
|
221
|
+
AsyncTemplate()
|
|
222
|
+
.from_python_image('3')
|
|
223
|
+
.copy('requirements.txt', '/home/user/')
|
|
224
|
+
.run_cmd('pip install -r /home/user/requirements.txt')
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Build with single tag
|
|
228
|
+
await AsyncTemplate.build(template, 'my-python-env:v1.0')
|
|
229
|
+
|
|
230
|
+
# Build with multiple tags
|
|
231
|
+
await AsyncTemplate.build(template, 'my-python-env', tags=['v1.1.0', 'stable'])
|
|
232
|
+
```
|
|
233
|
+
"""
|
|
234
|
+
name = normalize_build_arguments(name, alias)
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
if on_build_logs:
|
|
238
|
+
on_build_logs(
|
|
239
|
+
LogEntryStart(
|
|
240
|
+
timestamp=datetime.now(),
|
|
241
|
+
message="Build started",
|
|
242
|
+
)
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
config = ConnectionConfig(**opts)
|
|
246
|
+
api_client = get_api_client(
|
|
247
|
+
config,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
data = await AsyncTemplate._build(
|
|
251
|
+
api_client,
|
|
252
|
+
template,
|
|
253
|
+
name,
|
|
254
|
+
tags=tags,
|
|
255
|
+
cpu_count=cpu_count,
|
|
256
|
+
memory_mb=memory_mb,
|
|
257
|
+
skip_cache=skip_cache,
|
|
258
|
+
on_build_logs=on_build_logs,
|
|
259
|
+
# Only honor an explicitly set request_timeout for uploads;
|
|
260
|
+
# otherwise upload_file applies its 1-hour default.
|
|
261
|
+
request_timeout=opts.get("request_timeout"),
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
if on_build_logs:
|
|
265
|
+
on_build_logs(
|
|
266
|
+
LogEntry(
|
|
267
|
+
timestamp=datetime.now(),
|
|
268
|
+
level="info",
|
|
269
|
+
message="Waiting for logs...",
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
await wait_for_build_finish(
|
|
274
|
+
api_client,
|
|
275
|
+
data.template_id,
|
|
276
|
+
data.build_id,
|
|
277
|
+
on_build_logs,
|
|
278
|
+
logs_refresh_frequency=TemplateBase._logs_refresh_frequency,
|
|
279
|
+
stack_traces=template._template._stack_traces,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
return data
|
|
283
|
+
finally:
|
|
284
|
+
if on_build_logs:
|
|
285
|
+
on_build_logs(
|
|
286
|
+
LogEntryEnd(
|
|
287
|
+
timestamp=datetime.now(),
|
|
288
|
+
message="Build finished",
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
@staticmethod
|
|
293
|
+
async def build_in_background(
|
|
294
|
+
template: TemplateClass,
|
|
295
|
+
name: Optional[str] = None,
|
|
296
|
+
*,
|
|
297
|
+
alias: Optional[str] = None,
|
|
298
|
+
tags: Optional[List[str]] = None,
|
|
299
|
+
cpu_count: int = 2,
|
|
300
|
+
memory_mb: int = 1024,
|
|
301
|
+
skip_cache: bool = False,
|
|
302
|
+
on_build_logs: Optional[Callable[[LogEntry], None]] = None,
|
|
303
|
+
**opts: Unpack[ApiParams],
|
|
304
|
+
) -> BuildInfo:
|
|
305
|
+
"""
|
|
306
|
+
Build and deploy a template to Loopix infrastructure without waiting for completion.
|
|
307
|
+
|
|
308
|
+
:param template: The template to build
|
|
309
|
+
:param name: Template name in 'name' or 'name:tag' format
|
|
310
|
+
:param alias: (Deprecated) Alias name for the template. Use name instead.
|
|
311
|
+
:param tags: Optional additional tags to assign to the template
|
|
312
|
+
:param cpu_count: Number of CPUs allocated to the sandbox
|
|
313
|
+
:param memory_mb: Amount of memory in MB allocated to the sandbox
|
|
314
|
+
:param skip_cache: If True, forces a complete rebuild ignoring cache
|
|
315
|
+
:return: BuildInfo containing the template ID and build ID
|
|
316
|
+
|
|
317
|
+
Example
|
|
318
|
+
```python
|
|
319
|
+
from loopix import AsyncTemplate
|
|
320
|
+
|
|
321
|
+
template = (
|
|
322
|
+
AsyncTemplate()
|
|
323
|
+
.from_python_image('3')
|
|
324
|
+
.run_cmd('echo "test"')
|
|
325
|
+
.set_start_cmd('echo "Hello"', 'sleep 1')
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Build with single tag
|
|
329
|
+
build_info = await AsyncTemplate.build_in_background(template, 'my-python-env:v1.0')
|
|
330
|
+
|
|
331
|
+
# Build with multiple tags
|
|
332
|
+
build_info = await AsyncTemplate.build_in_background(template, 'my-python-env', tags=['v1.1.0', 'stable'])
|
|
333
|
+
```
|
|
334
|
+
"""
|
|
335
|
+
name = normalize_build_arguments(name, alias)
|
|
336
|
+
|
|
337
|
+
config = ConnectionConfig(**opts)
|
|
338
|
+
api_client = get_api_client(
|
|
339
|
+
config,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
return await AsyncTemplate._build(
|
|
343
|
+
api_client,
|
|
344
|
+
template,
|
|
345
|
+
name,
|
|
346
|
+
tags=tags,
|
|
347
|
+
cpu_count=cpu_count,
|
|
348
|
+
memory_mb=memory_mb,
|
|
349
|
+
skip_cache=skip_cache,
|
|
350
|
+
on_build_logs=on_build_logs,
|
|
351
|
+
# Only honor an explicitly set request_timeout for uploads;
|
|
352
|
+
# otherwise upload_file applies its 1-hour default.
|
|
353
|
+
request_timeout=opts.get("request_timeout"),
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
async def get_build_status(
|
|
358
|
+
build_info: BuildInfo,
|
|
359
|
+
logs_offset: int = 0,
|
|
360
|
+
**opts: Unpack[ApiParams],
|
|
361
|
+
):
|
|
362
|
+
"""
|
|
363
|
+
Get the status of a build.
|
|
364
|
+
|
|
365
|
+
:param build_info: Build identifiers returned from build_in_background
|
|
366
|
+
:param logs_offset: Offset for fetching logs
|
|
367
|
+
:return: TemplateBuild containing the build status and logs
|
|
368
|
+
|
|
369
|
+
Example
|
|
370
|
+
```python
|
|
371
|
+
from loopix import AsyncTemplate
|
|
372
|
+
|
|
373
|
+
build_info = await AsyncTemplate.build_in_background(template, alias='my-template')
|
|
374
|
+
status = await AsyncTemplate.get_build_status(build_info, logs_offset=0)
|
|
375
|
+
```
|
|
376
|
+
"""
|
|
377
|
+
config = ConnectionConfig(**opts)
|
|
378
|
+
api_client = get_api_client(
|
|
379
|
+
config,
|
|
380
|
+
)
|
|
381
|
+
return await get_build_status(
|
|
382
|
+
api_client,
|
|
383
|
+
build_info.template_id,
|
|
384
|
+
build_info.build_id,
|
|
385
|
+
logs_offset,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
@staticmethod
|
|
389
|
+
async def exists(
|
|
390
|
+
name: str,
|
|
391
|
+
**opts: Unpack[ApiParams],
|
|
392
|
+
) -> bool:
|
|
393
|
+
"""
|
|
394
|
+
Check if a template with the given name exists.
|
|
395
|
+
|
|
396
|
+
:param name: Template name to check
|
|
397
|
+
:return: True if the name exists, False otherwise
|
|
398
|
+
|
|
399
|
+
Example
|
|
400
|
+
```python
|
|
401
|
+
from loopix import AsyncTemplate
|
|
402
|
+
|
|
403
|
+
exists = await AsyncTemplate.exists('my-python-env')
|
|
404
|
+
if exists:
|
|
405
|
+
print('Template exists!')
|
|
406
|
+
```
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
return await AsyncTemplate.alias_exists(name, **opts)
|
|
410
|
+
|
|
411
|
+
@staticmethod
|
|
412
|
+
async def alias_exists(
|
|
413
|
+
alias: str,
|
|
414
|
+
**opts: Unpack[ApiParams],
|
|
415
|
+
) -> bool:
|
|
416
|
+
"""
|
|
417
|
+
Check if a template with the given alias exists.
|
|
418
|
+
|
|
419
|
+
Deprecated Use `exists` instead.
|
|
420
|
+
|
|
421
|
+
:param alias: Template alias to check
|
|
422
|
+
:return: True if the alias exists, False otherwise
|
|
423
|
+
|
|
424
|
+
Example
|
|
425
|
+
```python
|
|
426
|
+
from loopix import AsyncTemplate
|
|
427
|
+
|
|
428
|
+
exists = await AsyncTemplate.alias_exists('my-python-env')
|
|
429
|
+
if exists:
|
|
430
|
+
print('Template exists!')
|
|
431
|
+
```
|
|
432
|
+
"""
|
|
433
|
+
config = ConnectionConfig(**opts)
|
|
434
|
+
api_client = get_api_client(
|
|
435
|
+
config,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
return await check_alias_exists(api_client, alias)
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
async def assign_tags(
|
|
442
|
+
target_name: str,
|
|
443
|
+
tags: Union[str, List[str]],
|
|
444
|
+
**opts: Unpack[ApiParams],
|
|
445
|
+
) -> TemplateTagInfo:
|
|
446
|
+
"""
|
|
447
|
+
Assign tag(s) to an existing template build.
|
|
448
|
+
|
|
449
|
+
:param target_name: Template name in 'name:tag' format (the source build to tag from)
|
|
450
|
+
:param tags: Tag or tags to assign
|
|
451
|
+
:return: TemplateTagInfo with build_id and assigned tags
|
|
452
|
+
|
|
453
|
+
Example
|
|
454
|
+
```python
|
|
455
|
+
from loopix import AsyncTemplate
|
|
456
|
+
|
|
457
|
+
# Assign a single tag
|
|
458
|
+
result = await AsyncTemplate.assign_tags('my-template:v1.0', 'production')
|
|
459
|
+
|
|
460
|
+
# Assign multiple tags
|
|
461
|
+
result = await AsyncTemplate.assign_tags('my-template:v1.0', ['production', 'stable'])
|
|
462
|
+
```
|
|
463
|
+
"""
|
|
464
|
+
config = ConnectionConfig(**opts)
|
|
465
|
+
api_client = get_api_client(
|
|
466
|
+
config,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
normalized_tags = [tags] if isinstance(tags, str) else tags
|
|
470
|
+
return await assign_tags(api_client, target_name, normalized_tags)
|
|
471
|
+
|
|
472
|
+
@staticmethod
|
|
473
|
+
async def remove_tags(
|
|
474
|
+
name: str,
|
|
475
|
+
tags: Union[str, List[str]],
|
|
476
|
+
**opts: Unpack[ApiParams],
|
|
477
|
+
) -> None:
|
|
478
|
+
"""
|
|
479
|
+
Remove tag(s) from a template.
|
|
480
|
+
|
|
481
|
+
:param name: Template name
|
|
482
|
+
:param tags: Tag or tags to remove
|
|
483
|
+
|
|
484
|
+
Example
|
|
485
|
+
```python
|
|
486
|
+
from loopix import AsyncTemplate
|
|
487
|
+
|
|
488
|
+
# Remove a single tag
|
|
489
|
+
await AsyncTemplate.remove_tags('my-template', 'production')
|
|
490
|
+
|
|
491
|
+
# Remove multiple tags
|
|
492
|
+
await AsyncTemplate.remove_tags('my-template', ['production', 'stable'])
|
|
493
|
+
```
|
|
494
|
+
"""
|
|
495
|
+
config = ConnectionConfig(**opts)
|
|
496
|
+
api_client = get_api_client(
|
|
497
|
+
config,
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
normalized_tags = [tags] if isinstance(tags, str) else tags
|
|
501
|
+
await remove_tags(api_client, name, normalized_tags)
|
|
502
|
+
|
|
503
|
+
@staticmethod
|
|
504
|
+
async def get_tags(
|
|
505
|
+
template_id: str,
|
|
506
|
+
**opts: Unpack[ApiParams],
|
|
507
|
+
) -> List[TemplateTag]:
|
|
508
|
+
"""
|
|
509
|
+
Get all tags for a template.
|
|
510
|
+
|
|
511
|
+
:param template_id: Template ID or name
|
|
512
|
+
:return: List of TemplateTag with tag name, build_id, and created_at
|
|
513
|
+
|
|
514
|
+
Example
|
|
515
|
+
```python
|
|
516
|
+
from loopix import AsyncTemplate
|
|
517
|
+
|
|
518
|
+
tags = await AsyncTemplate.get_tags('my-template')
|
|
519
|
+
for tag in tags:
|
|
520
|
+
print(f"Tag: {tag.tag}, Build: {tag.build_id}, Created: {tag.created_at}")
|
|
521
|
+
```
|
|
522
|
+
"""
|
|
523
|
+
config = ConnectionConfig(**opts)
|
|
524
|
+
api_client = get_api_client(
|
|
525
|
+
config,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
return await get_template_tags(api_client, template_id)
|