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,529 @@
|
|
|
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
|
+
|
|
9
|
+
from loopix.api.client_sync import get_api_client
|
|
10
|
+
from loopix.template.consts import GZIP, RESOLVE_SYMLINKS
|
|
11
|
+
from loopix.template.logger import LogEntry, LogEntryEnd, LogEntryStart
|
|
12
|
+
from loopix.template.main import TemplateBase, TemplateClass
|
|
13
|
+
from loopix.template.types import BuildInfo, InstructionType, TemplateTag, TemplateTagInfo
|
|
14
|
+
from loopix.template_sync.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.template.utils import normalize_build_arguments, read_dockerignore
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Template(TemplateBase):
|
|
30
|
+
"""
|
|
31
|
+
Synchronous template builder for Loopix sandboxes.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
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 = 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 = 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
|
+
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
|
+
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
|
+
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 Template
|
|
219
|
+
|
|
220
|
+
template = (
|
|
221
|
+
Template()
|
|
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
|
+
Template.build(template, 'my-python-env:v1.0')
|
|
229
|
+
|
|
230
|
+
# Build with multiple tags
|
|
231
|
+
Template.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 = Template._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
|
+
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
|
+
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 Template
|
|
320
|
+
|
|
321
|
+
template = (
|
|
322
|
+
Template()
|
|
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 = Template.build_in_background(template, 'my-python-env:v1.0')
|
|
330
|
+
|
|
331
|
+
# Build with multiple tags
|
|
332
|
+
build_info = Template.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 Template._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
|
+
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 Template
|
|
372
|
+
|
|
373
|
+
build_info = Template.build_in_background(template, alias='my-template')
|
|
374
|
+
status = Template.get_build_status(build_info, logs_offset=0)
|
|
375
|
+
```
|
|
376
|
+
"""
|
|
377
|
+
config = ConnectionConfig(**opts)
|
|
378
|
+
api_client = get_api_client(
|
|
379
|
+
config,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
return get_build_status(
|
|
383
|
+
api_client,
|
|
384
|
+
build_info.template_id,
|
|
385
|
+
build_info.build_id,
|
|
386
|
+
logs_offset,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
@staticmethod
|
|
390
|
+
def exists(
|
|
391
|
+
name: str,
|
|
392
|
+
**opts: Unpack[ApiParams],
|
|
393
|
+
) -> bool:
|
|
394
|
+
"""
|
|
395
|
+
Check if a template with the given name exists.
|
|
396
|
+
|
|
397
|
+
:param name: Template name to check
|
|
398
|
+
:return: True if the name exists, False otherwise
|
|
399
|
+
|
|
400
|
+
Example
|
|
401
|
+
```python
|
|
402
|
+
from loopix import Template
|
|
403
|
+
|
|
404
|
+
exists = Template.exists('my-python-env')
|
|
405
|
+
if exists:
|
|
406
|
+
print('Template exists!')
|
|
407
|
+
```
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
return Template.alias_exists(name, **opts)
|
|
411
|
+
|
|
412
|
+
@staticmethod
|
|
413
|
+
def alias_exists(
|
|
414
|
+
alias: str,
|
|
415
|
+
**opts: Unpack[ApiParams],
|
|
416
|
+
) -> bool:
|
|
417
|
+
"""
|
|
418
|
+
Check if a template with the given alias exists.
|
|
419
|
+
|
|
420
|
+
Deprecated Use `exists` instead.
|
|
421
|
+
|
|
422
|
+
:param alias: Template alias to check
|
|
423
|
+
:return: True if the alias exists, False otherwise
|
|
424
|
+
|
|
425
|
+
Example
|
|
426
|
+
```python
|
|
427
|
+
from loopix import Template
|
|
428
|
+
|
|
429
|
+
exists = Template.alias_exists('my-python-env')
|
|
430
|
+
if exists:
|
|
431
|
+
print('Template exists!')
|
|
432
|
+
```
|
|
433
|
+
"""
|
|
434
|
+
config = ConnectionConfig(**opts)
|
|
435
|
+
api_client = get_api_client(
|
|
436
|
+
config,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
return check_alias_exists(api_client, alias)
|
|
440
|
+
|
|
441
|
+
@staticmethod
|
|
442
|
+
def assign_tags(
|
|
443
|
+
target_name: str,
|
|
444
|
+
tags: Union[str, List[str]],
|
|
445
|
+
**opts: Unpack[ApiParams],
|
|
446
|
+
) -> TemplateTagInfo:
|
|
447
|
+
"""
|
|
448
|
+
Assign tag(s) to an existing template build.
|
|
449
|
+
|
|
450
|
+
:param target_name: Template name in 'name:tag' format (the source build to tag from)
|
|
451
|
+
:param tags: Tag or tags to assign
|
|
452
|
+
:return: TemplateTagInfo with build_id and assigned tags
|
|
453
|
+
|
|
454
|
+
Example
|
|
455
|
+
```python
|
|
456
|
+
from loopix import Template
|
|
457
|
+
|
|
458
|
+
# Assign a single tag
|
|
459
|
+
result = Template.assign_tags('my-template:v1.0', 'production')
|
|
460
|
+
|
|
461
|
+
# Assign multiple tags
|
|
462
|
+
result = Template.assign_tags('my-template:v1.0', ['production', 'stable'])
|
|
463
|
+
```
|
|
464
|
+
"""
|
|
465
|
+
config = ConnectionConfig(**opts)
|
|
466
|
+
api_client = get_api_client(
|
|
467
|
+
config,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
normalized_tags = [tags] if isinstance(tags, str) else tags
|
|
471
|
+
return assign_tags(api_client, target_name, normalized_tags)
|
|
472
|
+
|
|
473
|
+
@staticmethod
|
|
474
|
+
def remove_tags(
|
|
475
|
+
name: str,
|
|
476
|
+
tags: Union[str, List[str]],
|
|
477
|
+
**opts: Unpack[ApiParams],
|
|
478
|
+
) -> None:
|
|
479
|
+
"""
|
|
480
|
+
Remove tag(s) from a template.
|
|
481
|
+
|
|
482
|
+
:param name: Template name
|
|
483
|
+
:param tags: Tag or tags to remove
|
|
484
|
+
|
|
485
|
+
Example
|
|
486
|
+
```python
|
|
487
|
+
from loopix import Template
|
|
488
|
+
|
|
489
|
+
# Remove a single tag
|
|
490
|
+
Template.remove_tags('my-template', 'production')
|
|
491
|
+
|
|
492
|
+
# Remove multiple tags
|
|
493
|
+
Template.remove_tags('my-template', ['production', 'stable'])
|
|
494
|
+
```
|
|
495
|
+
"""
|
|
496
|
+
config = ConnectionConfig(**opts)
|
|
497
|
+
api_client = get_api_client(
|
|
498
|
+
config,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
normalized_tags = [tags] if isinstance(tags, str) else tags
|
|
502
|
+
remove_tags(api_client, name, normalized_tags)
|
|
503
|
+
|
|
504
|
+
@staticmethod
|
|
505
|
+
def get_tags(
|
|
506
|
+
template_id: str,
|
|
507
|
+
**opts: Unpack[ApiParams],
|
|
508
|
+
) -> List[TemplateTag]:
|
|
509
|
+
"""
|
|
510
|
+
Get all tags for a template.
|
|
511
|
+
|
|
512
|
+
:param template_id: Template ID or name
|
|
513
|
+
:return: List of TemplateTag with tag name, build_id, and created_at
|
|
514
|
+
|
|
515
|
+
Example
|
|
516
|
+
```python
|
|
517
|
+
from loopix import Template
|
|
518
|
+
|
|
519
|
+
tags = Template.get_tags('my-template')
|
|
520
|
+
for tag in tags:
|
|
521
|
+
print(f"Tag: {tag.tag}, Build: {tag.build_id}, Created: {tag.created_at}")
|
|
522
|
+
```
|
|
523
|
+
"""
|
|
524
|
+
config = ConnectionConfig(**opts)
|
|
525
|
+
api_client = get_api_client(
|
|
526
|
+
config,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
return get_template_tags(api_client, template_id)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any, Optional, Union, cast
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from ... import errors
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...models.error import Error
|
|
9
|
+
from ...types import UNSET, Response
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_kwargs(
|
|
13
|
+
volume_id: str,
|
|
14
|
+
*,
|
|
15
|
+
path: str,
|
|
16
|
+
) -> dict[str, Any]:
|
|
17
|
+
params: dict[str, Any] = {}
|
|
18
|
+
|
|
19
|
+
params["path"] = path
|
|
20
|
+
|
|
21
|
+
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
|
22
|
+
|
|
23
|
+
_kwargs: dict[str, Any] = {
|
|
24
|
+
"method": "delete",
|
|
25
|
+
"url": f"/volumecontent/{volume_id}/path",
|
|
26
|
+
"params": params,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return _kwargs
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _parse_response(
|
|
33
|
+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
|
34
|
+
) -> Optional[Union[Any, Error]]:
|
|
35
|
+
if response.status_code == 204:
|
|
36
|
+
response_204 = cast(Any, None)
|
|
37
|
+
return response_204
|
|
38
|
+
if response.status_code == 404:
|
|
39
|
+
response_404 = Error.from_dict(response.json())
|
|
40
|
+
|
|
41
|
+
return response_404
|
|
42
|
+
if client.raise_on_unexpected_status:
|
|
43
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
44
|
+
else:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _build_response(
|
|
49
|
+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
|
50
|
+
) -> Response[Union[Any, Error]]:
|
|
51
|
+
return Response(
|
|
52
|
+
status_code=HTTPStatus(response.status_code),
|
|
53
|
+
content=response.content,
|
|
54
|
+
headers=response.headers,
|
|
55
|
+
parsed=_parse_response(client=client, response=response),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def sync_detailed(
|
|
60
|
+
volume_id: str,
|
|
61
|
+
*,
|
|
62
|
+
client: Union[AuthenticatedClient, Client],
|
|
63
|
+
path: str,
|
|
64
|
+
) -> Response[Union[Any, Error]]:
|
|
65
|
+
"""Delete a path
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
volume_id (str):
|
|
69
|
+
path (str):
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
73
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Response[Union[Any, Error]]
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
kwargs = _get_kwargs(
|
|
80
|
+
volume_id=volume_id,
|
|
81
|
+
path=path,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
response = client.get_httpx_client().request(
|
|
85
|
+
**kwargs,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return _build_response(client=client, response=response)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def sync(
|
|
92
|
+
volume_id: str,
|
|
93
|
+
*,
|
|
94
|
+
client: Union[AuthenticatedClient, Client],
|
|
95
|
+
path: str,
|
|
96
|
+
) -> Optional[Union[Any, Error]]:
|
|
97
|
+
"""Delete a path
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
volume_id (str):
|
|
101
|
+
path (str):
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
105
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Union[Any, Error]
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
return sync_detailed(
|
|
112
|
+
volume_id=volume_id,
|
|
113
|
+
client=client,
|
|
114
|
+
path=path,
|
|
115
|
+
).parsed
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def asyncio_detailed(
|
|
119
|
+
volume_id: str,
|
|
120
|
+
*,
|
|
121
|
+
client: Union[AuthenticatedClient, Client],
|
|
122
|
+
path: str,
|
|
123
|
+
) -> Response[Union[Any, Error]]:
|
|
124
|
+
"""Delete a path
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
volume_id (str):
|
|
128
|
+
path (str):
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
132
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Response[Union[Any, Error]]
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
kwargs = _get_kwargs(
|
|
139
|
+
volume_id=volume_id,
|
|
140
|
+
path=path,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
144
|
+
|
|
145
|
+
return _build_response(client=client, response=response)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async def asyncio(
|
|
149
|
+
volume_id: str,
|
|
150
|
+
*,
|
|
151
|
+
client: Union[AuthenticatedClient, Client],
|
|
152
|
+
path: str,
|
|
153
|
+
) -> Optional[Union[Any, Error]]:
|
|
154
|
+
"""Delete a path
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
volume_id (str):
|
|
158
|
+
path (str):
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
162
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Union[Any, Error]
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
await asyncio_detailed(
|
|
170
|
+
volume_id=volume_id,
|
|
171
|
+
client=client,
|
|
172
|
+
path=path,
|
|
173
|
+
)
|
|
174
|
+
).parsed
|