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,140 @@
|
|
|
1
|
+
import urllib.parse
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
from typing_extensions import Unpack
|
|
5
|
+
|
|
6
|
+
from loopix.api import handle_api_exception
|
|
7
|
+
from loopix.api.client.api.sandboxes import get_v2_sandboxes
|
|
8
|
+
from loopix.api.client.api.snapshots import get_snapshots
|
|
9
|
+
from loopix.api.client.models.error import Error
|
|
10
|
+
from loopix.api.client.types import UNSET
|
|
11
|
+
from loopix.connection_config import ApiParams, ConnectionConfig
|
|
12
|
+
from loopix.exceptions import SandboxException
|
|
13
|
+
from loopix.sandbox.sandbox_api import (
|
|
14
|
+
SandboxPaginatorBase,
|
|
15
|
+
SandboxInfo,
|
|
16
|
+
SnapshotPaginatorBase,
|
|
17
|
+
SnapshotInfo,
|
|
18
|
+
)
|
|
19
|
+
from loopix.api.client_sync import get_api_client
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SandboxPaginator(SandboxPaginatorBase):
|
|
23
|
+
"""
|
|
24
|
+
Paginator for listing sandboxes.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
```python
|
|
28
|
+
paginator = Sandbox.list()
|
|
29
|
+
|
|
30
|
+
while paginator.has_next:
|
|
31
|
+
sandboxes = paginator.next_items()
|
|
32
|
+
print(sandboxes)
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def next_items(self, **opts: Unpack[ApiParams]) -> List[SandboxInfo]:
|
|
37
|
+
"""
|
|
38
|
+
Returns the next page of sandboxes.
|
|
39
|
+
|
|
40
|
+
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
|
|
41
|
+
|
|
42
|
+
:param opts: Per-call connection options (e.g. `api_key`, `domain`,
|
|
43
|
+
`headers`, `request_timeout`). When provided, this call uses these
|
|
44
|
+
options instead of the ones the paginator was constructed with.
|
|
45
|
+
|
|
46
|
+
:returns: List of sandboxes
|
|
47
|
+
"""
|
|
48
|
+
if not self.has_next:
|
|
49
|
+
raise Exception("No more items to fetch")
|
|
50
|
+
|
|
51
|
+
# Convert filters to the format expected by the API
|
|
52
|
+
metadata: Optional[str] = None
|
|
53
|
+
if self.query and self.query.metadata:
|
|
54
|
+
quoted_metadata = {
|
|
55
|
+
urllib.parse.quote(k): urllib.parse.quote(v)
|
|
56
|
+
for k, v in self.query.metadata.items()
|
|
57
|
+
}
|
|
58
|
+
metadata = urllib.parse.urlencode(quoted_metadata)
|
|
59
|
+
|
|
60
|
+
config = ConnectionConfig(**{**self._opts, **opts})
|
|
61
|
+
api_client = get_api_client(config)
|
|
62
|
+
res = get_v2_sandboxes.sync_detailed(
|
|
63
|
+
client=api_client,
|
|
64
|
+
metadata=metadata if metadata else UNSET,
|
|
65
|
+
state=self.query.state if self.query and self.query.state else UNSET,
|
|
66
|
+
limit=self.limit if self.limit else UNSET,
|
|
67
|
+
next_token=self._next_token if self._next_token else UNSET,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if res.status_code >= 300:
|
|
71
|
+
raise handle_api_exception(res)
|
|
72
|
+
|
|
73
|
+
self._update_pagination(res.headers)
|
|
74
|
+
|
|
75
|
+
if res.parsed is None:
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
# Check if res.parsed is Error
|
|
79
|
+
if isinstance(res.parsed, Error):
|
|
80
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
81
|
+
|
|
82
|
+
return [SandboxInfo._from_listed_sandbox(sandbox) for sandbox in res.parsed]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class SnapshotPaginator(SnapshotPaginatorBase):
|
|
86
|
+
"""
|
|
87
|
+
Paginator for listing snapshots.
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
```python
|
|
91
|
+
paginator = Sandbox.list_snapshots()
|
|
92
|
+
|
|
93
|
+
while paginator.has_next:
|
|
94
|
+
snapshots = paginator.next_items()
|
|
95
|
+
print(snapshots)
|
|
96
|
+
```
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def next_items(self, **opts: Unpack[ApiParams]) -> List[SnapshotInfo]:
|
|
100
|
+
"""
|
|
101
|
+
Returns the next page of snapshots.
|
|
102
|
+
|
|
103
|
+
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
|
|
104
|
+
|
|
105
|
+
:param opts: Per-call connection options (e.g. `api_key`, `domain`,
|
|
106
|
+
`headers`, `request_timeout`). When provided, this call uses these
|
|
107
|
+
options instead of the ones the paginator was constructed with.
|
|
108
|
+
|
|
109
|
+
:returns: List of snapshots
|
|
110
|
+
"""
|
|
111
|
+
if not self.has_next:
|
|
112
|
+
raise Exception("No more items to fetch")
|
|
113
|
+
|
|
114
|
+
config = ConnectionConfig(**{**self._opts, **opts})
|
|
115
|
+
api_client = get_api_client(config)
|
|
116
|
+
res = get_snapshots.sync_detailed(
|
|
117
|
+
client=api_client,
|
|
118
|
+
sandbox_id=self.sandbox_id if self.sandbox_id else UNSET,
|
|
119
|
+
limit=self.limit if self.limit else UNSET,
|
|
120
|
+
next_token=self._next_token if self._next_token else UNSET,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if res.status_code >= 300:
|
|
124
|
+
raise handle_api_exception(res)
|
|
125
|
+
|
|
126
|
+
self._update_pagination(res.headers)
|
|
127
|
+
|
|
128
|
+
if res.parsed is None:
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
if isinstance(res.parsed, Error):
|
|
132
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
133
|
+
|
|
134
|
+
return [
|
|
135
|
+
SnapshotInfo(
|
|
136
|
+
snapshot_id=snapshot.snapshot_id,
|
|
137
|
+
names=list(snapshot.names) if snapshot.names else [],
|
|
138
|
+
)
|
|
139
|
+
for snapshot in res.parsed
|
|
140
|
+
]
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, Dict, List, Optional, cast
|
|
4
|
+
|
|
5
|
+
from packaging.version import Version
|
|
6
|
+
from typing_extensions import Unpack
|
|
7
|
+
|
|
8
|
+
from loopix.api import SandboxCreateResponse, handle_api_exception
|
|
9
|
+
from loopix.api.client.api.sandboxes import (
|
|
10
|
+
delete_sandboxes_sandbox_id,
|
|
11
|
+
get_sandboxes_sandbox_id,
|
|
12
|
+
get_sandboxes_sandbox_id_metrics,
|
|
13
|
+
post_sandboxes,
|
|
14
|
+
post_sandboxes_sandbox_id_connect,
|
|
15
|
+
post_sandboxes_sandbox_id_pause,
|
|
16
|
+
post_sandboxes_sandbox_id_snapshots,
|
|
17
|
+
post_sandboxes_sandbox_id_timeout,
|
|
18
|
+
put_sandboxes_sandbox_id_network,
|
|
19
|
+
)
|
|
20
|
+
from loopix.api.client.api.templates import delete_templates_template_id
|
|
21
|
+
from loopix.api.client.models import (
|
|
22
|
+
ConnectSandbox,
|
|
23
|
+
Error,
|
|
24
|
+
NewSandbox,
|
|
25
|
+
PostSandboxesSandboxIDSnapshotsBody,
|
|
26
|
+
PostSandboxesSandboxIDTimeoutBody,
|
|
27
|
+
SandboxAutoResumeConfig,
|
|
28
|
+
SandboxNetworkConfig,
|
|
29
|
+
SandboxPauseRequest,
|
|
30
|
+
SandboxVolumeMount as SandboxVolumeMountAPI,
|
|
31
|
+
)
|
|
32
|
+
from loopix.api.client.types import UNSET
|
|
33
|
+
from loopix.connection_config import ApiParams, ConnectionConfig
|
|
34
|
+
from loopix.exceptions import (
|
|
35
|
+
InvalidArgumentException,
|
|
36
|
+
SandboxException,
|
|
37
|
+
SandboxNotFoundException,
|
|
38
|
+
TemplateException,
|
|
39
|
+
)
|
|
40
|
+
from loopix.sandbox.main import SandboxBase
|
|
41
|
+
from loopix.sandbox.sandbox_api import (
|
|
42
|
+
build_network_update_body,
|
|
43
|
+
McpServer,
|
|
44
|
+
SandboxInfo,
|
|
45
|
+
SandboxLifecycle,
|
|
46
|
+
SandboxMetrics,
|
|
47
|
+
SandboxNetworkOpts,
|
|
48
|
+
SandboxNetworkUpdate,
|
|
49
|
+
SandboxQuery,
|
|
50
|
+
SnapshotInfo,
|
|
51
|
+
build_network_config,
|
|
52
|
+
)
|
|
53
|
+
from loopix.sandbox_sync.paginator import SandboxPaginator, get_api_client
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SandboxApi(SandboxBase):
|
|
57
|
+
@staticmethod
|
|
58
|
+
def list(
|
|
59
|
+
query: Optional[SandboxQuery] = None,
|
|
60
|
+
limit: Optional[int] = None,
|
|
61
|
+
next_token: Optional[str] = None,
|
|
62
|
+
**opts: Unpack[ApiParams],
|
|
63
|
+
) -> SandboxPaginator:
|
|
64
|
+
"""
|
|
65
|
+
List sandboxes.
|
|
66
|
+
|
|
67
|
+
By default (no `query.state` set), returns sandboxes in both `running`
|
|
68
|
+
and `paused` states. To filter by state, pass `query=SandboxQuery(state=[...])`.
|
|
69
|
+
|
|
70
|
+
:param query: Filter the list of sandboxes by metadata or state, e.g. `SandboxQuery(metadata={"key": "value"})` or `SandboxQuery(state=[SandboxState.RUNNING])`
|
|
71
|
+
:param limit: Maximum number of sandboxes to return per page
|
|
72
|
+
:param next_token: Token for pagination
|
|
73
|
+
|
|
74
|
+
:return: A `SandboxPaginator` that yields pages of sandboxes (running and paused by default). Iterate pages via `paginator.next_items()` while `paginator.has_next` is True.
|
|
75
|
+
"""
|
|
76
|
+
return SandboxPaginator(
|
|
77
|
+
query=query,
|
|
78
|
+
limit=limit,
|
|
79
|
+
next_token=next_token,
|
|
80
|
+
**opts,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def _cls_get_info(
|
|
85
|
+
cls,
|
|
86
|
+
sandbox_id: str,
|
|
87
|
+
**opts: Unpack[ApiParams],
|
|
88
|
+
) -> SandboxInfo:
|
|
89
|
+
"""
|
|
90
|
+
Get the sandbox info.
|
|
91
|
+
:param sandbox_id: Sandbox ID
|
|
92
|
+
|
|
93
|
+
:return: Sandbox info
|
|
94
|
+
"""
|
|
95
|
+
config = ConnectionConfig(**opts)
|
|
96
|
+
|
|
97
|
+
api_client = get_api_client(config)
|
|
98
|
+
res = get_sandboxes_sandbox_id.sync_detailed(
|
|
99
|
+
sandbox_id,
|
|
100
|
+
client=api_client,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if res.status_code == 404:
|
|
104
|
+
raise SandboxNotFoundException(f"Sandbox {sandbox_id} not found")
|
|
105
|
+
|
|
106
|
+
if res.status_code >= 300:
|
|
107
|
+
raise handle_api_exception(res)
|
|
108
|
+
|
|
109
|
+
if res.parsed is None:
|
|
110
|
+
raise Exception("Body of the request is None")
|
|
111
|
+
|
|
112
|
+
if isinstance(res.parsed, Error):
|
|
113
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
114
|
+
|
|
115
|
+
return SandboxInfo._from_sandbox_detail(res.parsed)
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def _cls_kill(
|
|
119
|
+
cls,
|
|
120
|
+
sandbox_id: str,
|
|
121
|
+
**opts: Unpack[ApiParams],
|
|
122
|
+
) -> bool:
|
|
123
|
+
config = ConnectionConfig(**opts)
|
|
124
|
+
|
|
125
|
+
if config.debug:
|
|
126
|
+
# Skip killing the sandbox in debug mode
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
api_client = get_api_client(config)
|
|
130
|
+
res = delete_sandboxes_sandbox_id.sync_detailed(
|
|
131
|
+
sandbox_id,
|
|
132
|
+
client=api_client,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if res.status_code == 404:
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
if res.status_code >= 300:
|
|
139
|
+
raise handle_api_exception(res)
|
|
140
|
+
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def _cls_set_timeout(
|
|
145
|
+
cls,
|
|
146
|
+
sandbox_id: str,
|
|
147
|
+
timeout: int,
|
|
148
|
+
**opts: Unpack[ApiParams],
|
|
149
|
+
) -> None:
|
|
150
|
+
config = ConnectionConfig(**opts)
|
|
151
|
+
|
|
152
|
+
if config.debug:
|
|
153
|
+
# Skip setting timeout in debug mode
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
api_client = get_api_client(config)
|
|
157
|
+
res = post_sandboxes_sandbox_id_timeout.sync_detailed(
|
|
158
|
+
sandbox_id,
|
|
159
|
+
client=api_client,
|
|
160
|
+
body=PostSandboxesSandboxIDTimeoutBody(timeout=timeout),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if res.status_code == 404:
|
|
164
|
+
raise SandboxNotFoundException(f"Sandbox {sandbox_id} not found")
|
|
165
|
+
|
|
166
|
+
if res.status_code >= 300:
|
|
167
|
+
raise handle_api_exception(res)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def _cls_update_network(
|
|
171
|
+
cls,
|
|
172
|
+
sandbox_id: str,
|
|
173
|
+
network: SandboxNetworkUpdate,
|
|
174
|
+
**opts: Unpack[ApiParams],
|
|
175
|
+
) -> None:
|
|
176
|
+
config = ConnectionConfig(**opts)
|
|
177
|
+
|
|
178
|
+
api_client = get_api_client(config)
|
|
179
|
+
res = put_sandboxes_sandbox_id_network.sync_detailed(
|
|
180
|
+
sandbox_id,
|
|
181
|
+
client=api_client,
|
|
182
|
+
body=build_network_update_body(network),
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if res.status_code == 404:
|
|
186
|
+
raise SandboxNotFoundException(f"Sandbox {sandbox_id} not found")
|
|
187
|
+
|
|
188
|
+
if res.status_code >= 300:
|
|
189
|
+
raise handle_api_exception(res)
|
|
190
|
+
|
|
191
|
+
@classmethod
|
|
192
|
+
def _create_sandbox(
|
|
193
|
+
cls,
|
|
194
|
+
template: str,
|
|
195
|
+
timeout: int,
|
|
196
|
+
allow_internet_access: bool,
|
|
197
|
+
metadata: Optional[Dict[str, str]],
|
|
198
|
+
env_vars: Optional[Dict[str, str]],
|
|
199
|
+
secure: bool,
|
|
200
|
+
mcp: Optional[McpServer] = None,
|
|
201
|
+
network: Optional[SandboxNetworkOpts] = None,
|
|
202
|
+
lifecycle: Optional[SandboxLifecycle] = None,
|
|
203
|
+
volume_mounts: Optional[List[SandboxVolumeMountAPI]] = None,
|
|
204
|
+
logger: Optional[logging.Logger] = None,
|
|
205
|
+
**opts: Unpack[ApiParams],
|
|
206
|
+
) -> SandboxCreateResponse:
|
|
207
|
+
config = ConnectionConfig(logger=logger, **opts)
|
|
208
|
+
|
|
209
|
+
# on_timeout accepts a bare action or {"action", "keep_memory"}; normalize.
|
|
210
|
+
# Only the object form carries keep_memory; anything else (a bare action
|
|
211
|
+
# string, or an unexpected value from an untyped caller) passes through as
|
|
212
|
+
# the action, so a non-"pause" value resolves to kill instead of crashing.
|
|
213
|
+
on_timeout_raw = lifecycle.get("on_timeout", "kill") if lifecycle else "kill"
|
|
214
|
+
if isinstance(on_timeout_raw, dict):
|
|
215
|
+
on_timeout = on_timeout_raw.get("action", "kill")
|
|
216
|
+
keep_memory_provided = "keep_memory" in on_timeout_raw
|
|
217
|
+
keep_memory = on_timeout_raw.get("keep_memory")
|
|
218
|
+
else:
|
|
219
|
+
on_timeout = on_timeout_raw
|
|
220
|
+
keep_memory = None
|
|
221
|
+
keep_memory_provided = False
|
|
222
|
+
|
|
223
|
+
# keep_memory only governs a pause action. The discriminated union type
|
|
224
|
+
# forbids it on action="kill"; re-check at runtime for callers that
|
|
225
|
+
# bypass the type.
|
|
226
|
+
if keep_memory_provided and on_timeout != "pause":
|
|
227
|
+
raise InvalidArgumentException(
|
|
228
|
+
"keep_memory is only allowed when on_timeout action is 'pause'."
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# A missing or explicit None keep_memory defaults to True (full memory),
|
|
232
|
+
# mirroring the JS SDK; sending null would wrongly read as filesystem-only.
|
|
233
|
+
if keep_memory is None:
|
|
234
|
+
keep_memory = True
|
|
235
|
+
auto_resume = lifecycle.get("auto_resume", False) if lifecycle else False
|
|
236
|
+
|
|
237
|
+
if auto_resume and on_timeout != "pause":
|
|
238
|
+
raise InvalidArgumentException(
|
|
239
|
+
"auto_resume can only be True when on_timeout action is 'pause'."
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not keep_memory and auto_resume:
|
|
243
|
+
raise InvalidArgumentException(
|
|
244
|
+
"auto_resume: True is not a valid value when keep_memory: False - "
|
|
245
|
+
"a filesystem-only snapshot cannot be auto-resumed by traffic and "
|
|
246
|
+
"must be resumed explicitly using Sandbox.connect()."
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
network_body = build_network_config(network)
|
|
250
|
+
body = NewSandbox(
|
|
251
|
+
template_id=template,
|
|
252
|
+
auto_pause=on_timeout == "pause",
|
|
253
|
+
auto_pause_memory=keep_memory if on_timeout == "pause" else UNSET,
|
|
254
|
+
auto_resume=SandboxAutoResumeConfig(enabled=auto_resume),
|
|
255
|
+
metadata=metadata or {},
|
|
256
|
+
timeout=timeout,
|
|
257
|
+
env_vars=env_vars or {},
|
|
258
|
+
mcp=cast(Any, mcp) or UNSET,
|
|
259
|
+
secure=secure,
|
|
260
|
+
allow_internet_access=allow_internet_access,
|
|
261
|
+
network=SandboxNetworkConfig(**network_body) if network_body else UNSET,
|
|
262
|
+
volume_mounts=volume_mounts if volume_mounts else UNSET,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
api_client = get_api_client(config)
|
|
266
|
+
res = post_sandboxes.sync_detailed(
|
|
267
|
+
body=body,
|
|
268
|
+
client=api_client,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
if res.status_code >= 300:
|
|
272
|
+
raise handle_api_exception(res)
|
|
273
|
+
|
|
274
|
+
if res.parsed is None:
|
|
275
|
+
raise Exception("Body of the request is None")
|
|
276
|
+
|
|
277
|
+
if isinstance(res.parsed, Error):
|
|
278
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
279
|
+
|
|
280
|
+
if Version(res.parsed.envd_version) < Version("0.1.0"):
|
|
281
|
+
SandboxApi._cls_kill(res.parsed.sandbox_id)
|
|
282
|
+
raise TemplateException(
|
|
283
|
+
"You need to update the template to use the new SDK."
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
domain = res.parsed.domain if isinstance(res.parsed.domain, str) else None
|
|
287
|
+
envd_token = (
|
|
288
|
+
res.parsed.envd_access_token
|
|
289
|
+
if isinstance(res.parsed.envd_access_token, str)
|
|
290
|
+
else None
|
|
291
|
+
)
|
|
292
|
+
traffic_token = (
|
|
293
|
+
res.parsed.traffic_access_token
|
|
294
|
+
if isinstance(res.parsed.traffic_access_token, str)
|
|
295
|
+
else None
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
return SandboxCreateResponse(
|
|
299
|
+
sandbox_id=res.parsed.sandbox_id,
|
|
300
|
+
sandbox_domain=domain,
|
|
301
|
+
envd_version=res.parsed.envd_version,
|
|
302
|
+
envd_access_token=envd_token,
|
|
303
|
+
traffic_access_token=traffic_token,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
@classmethod
|
|
307
|
+
def _cls_get_metrics(
|
|
308
|
+
cls,
|
|
309
|
+
sandbox_id: str,
|
|
310
|
+
start: Optional[datetime.datetime] = None,
|
|
311
|
+
end: Optional[datetime.datetime] = None,
|
|
312
|
+
**opts: Unpack[ApiParams],
|
|
313
|
+
) -> List[SandboxMetrics]:
|
|
314
|
+
config = ConnectionConfig(**opts)
|
|
315
|
+
|
|
316
|
+
if config.debug:
|
|
317
|
+
# Skip getting the metrics in debug mode
|
|
318
|
+
return []
|
|
319
|
+
|
|
320
|
+
api_client = get_api_client(config)
|
|
321
|
+
res = get_sandboxes_sandbox_id_metrics.sync_detailed(
|
|
322
|
+
sandbox_id,
|
|
323
|
+
start=int(start.timestamp()) if start else UNSET,
|
|
324
|
+
end=int(end.timestamp()) if end else UNSET,
|
|
325
|
+
client=api_client,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
if res.status_code == 404:
|
|
329
|
+
raise SandboxNotFoundException(f"Sandbox {sandbox_id} not found")
|
|
330
|
+
|
|
331
|
+
if res.status_code >= 300:
|
|
332
|
+
raise handle_api_exception(res)
|
|
333
|
+
|
|
334
|
+
if res.parsed is None:
|
|
335
|
+
return []
|
|
336
|
+
|
|
337
|
+
if isinstance(res.parsed, Error):
|
|
338
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
339
|
+
|
|
340
|
+
# Convert to typed SandboxMetrics objects
|
|
341
|
+
return [
|
|
342
|
+
SandboxMetrics(
|
|
343
|
+
cpu_count=metric.cpu_count,
|
|
344
|
+
cpu_used_pct=metric.cpu_used_pct,
|
|
345
|
+
disk_total=metric.disk_total,
|
|
346
|
+
disk_used=metric.disk_used,
|
|
347
|
+
mem_total=metric.mem_total,
|
|
348
|
+
mem_used=metric.mem_used,
|
|
349
|
+
mem_cache=metric.mem_cache,
|
|
350
|
+
timestamp=metric.timestamp,
|
|
351
|
+
)
|
|
352
|
+
for metric in res.parsed
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
@classmethod
|
|
356
|
+
def _cls_connect(
|
|
357
|
+
cls,
|
|
358
|
+
sandbox_id: str,
|
|
359
|
+
timeout: Optional[int] = None,
|
|
360
|
+
logger: Optional[logging.Logger] = None,
|
|
361
|
+
**opts: Unpack[ApiParams],
|
|
362
|
+
) -> SandboxCreateResponse:
|
|
363
|
+
timeout = timeout or SandboxBase.default_sandbox_timeout
|
|
364
|
+
|
|
365
|
+
config = ConnectionConfig(logger=logger, **opts)
|
|
366
|
+
|
|
367
|
+
api_client = get_api_client(config)
|
|
368
|
+
res = post_sandboxes_sandbox_id_connect.sync_detailed(
|
|
369
|
+
sandbox_id,
|
|
370
|
+
client=api_client,
|
|
371
|
+
body=ConnectSandbox(timeout=timeout),
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if res.status_code == 404:
|
|
375
|
+
raise SandboxNotFoundException(f"Paused sandbox {sandbox_id} not found")
|
|
376
|
+
|
|
377
|
+
if res.status_code >= 300:
|
|
378
|
+
raise handle_api_exception(res)
|
|
379
|
+
|
|
380
|
+
if isinstance(res.parsed, Error):
|
|
381
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
382
|
+
|
|
383
|
+
if res.parsed is None:
|
|
384
|
+
raise Exception("Body of the request is None")
|
|
385
|
+
|
|
386
|
+
domain = res.parsed.domain if isinstance(res.parsed.domain, str) else None
|
|
387
|
+
envd_token = (
|
|
388
|
+
res.parsed.envd_access_token
|
|
389
|
+
if isinstance(res.parsed.envd_access_token, str)
|
|
390
|
+
else None
|
|
391
|
+
)
|
|
392
|
+
traffic_token = (
|
|
393
|
+
res.parsed.traffic_access_token
|
|
394
|
+
if isinstance(res.parsed.traffic_access_token, str)
|
|
395
|
+
else None
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
return SandboxCreateResponse(
|
|
399
|
+
sandbox_id=res.parsed.sandbox_id,
|
|
400
|
+
sandbox_domain=domain,
|
|
401
|
+
envd_version=res.parsed.envd_version,
|
|
402
|
+
envd_access_token=envd_token,
|
|
403
|
+
traffic_access_token=traffic_token,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
@classmethod
|
|
407
|
+
def _cls_create_snapshot(
|
|
408
|
+
cls,
|
|
409
|
+
sandbox_id: str,
|
|
410
|
+
name: Optional[str] = None,
|
|
411
|
+
**opts: Unpack[ApiParams],
|
|
412
|
+
) -> SnapshotInfo:
|
|
413
|
+
config = ConnectionConfig(**opts)
|
|
414
|
+
|
|
415
|
+
api_client = get_api_client(config)
|
|
416
|
+
res = post_sandboxes_sandbox_id_snapshots.sync_detailed(
|
|
417
|
+
sandbox_id,
|
|
418
|
+
client=api_client,
|
|
419
|
+
body=PostSandboxesSandboxIDSnapshotsBody(name=name if name else UNSET),
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
if res.status_code == 404:
|
|
423
|
+
raise SandboxNotFoundException(f"Sandbox {sandbox_id} not found")
|
|
424
|
+
|
|
425
|
+
if res.status_code >= 300:
|
|
426
|
+
raise handle_api_exception(res)
|
|
427
|
+
|
|
428
|
+
if res.parsed is None:
|
|
429
|
+
raise Exception("Body of the request is None")
|
|
430
|
+
|
|
431
|
+
if isinstance(res.parsed, Error):
|
|
432
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
433
|
+
|
|
434
|
+
return SnapshotInfo(
|
|
435
|
+
snapshot_id=res.parsed.snapshot_id,
|
|
436
|
+
names=list(res.parsed.names) if res.parsed.names else [],
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
@classmethod
|
|
440
|
+
def _cls_delete_snapshot(
|
|
441
|
+
cls,
|
|
442
|
+
snapshot_id: str,
|
|
443
|
+
**opts: Unpack[ApiParams],
|
|
444
|
+
) -> bool:
|
|
445
|
+
config = ConnectionConfig(**opts)
|
|
446
|
+
|
|
447
|
+
api_client = get_api_client(config)
|
|
448
|
+
res = delete_templates_template_id.sync_detailed(
|
|
449
|
+
snapshot_id,
|
|
450
|
+
client=api_client,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
if res.status_code == 404:
|
|
454
|
+
return False
|
|
455
|
+
|
|
456
|
+
if res.status_code >= 300:
|
|
457
|
+
raise handle_api_exception(res)
|
|
458
|
+
|
|
459
|
+
return True
|
|
460
|
+
|
|
461
|
+
@classmethod
|
|
462
|
+
def _cls_pause(
|
|
463
|
+
cls,
|
|
464
|
+
sandbox_id: str,
|
|
465
|
+
keep_memory: bool = True,
|
|
466
|
+
**opts: Unpack[ApiParams],
|
|
467
|
+
) -> bool:
|
|
468
|
+
config = ConnectionConfig(**opts)
|
|
469
|
+
|
|
470
|
+
api_client = get_api_client(config)
|
|
471
|
+
res = post_sandboxes_sandbox_id_pause.sync_detailed(
|
|
472
|
+
sandbox_id,
|
|
473
|
+
client=api_client,
|
|
474
|
+
body=SandboxPauseRequest(memory=keep_memory),
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
if res.status_code == 404:
|
|
478
|
+
raise SandboxNotFoundException(f"Sandbox {sandbox_id} not found")
|
|
479
|
+
|
|
480
|
+
if res.status_code == 409:
|
|
481
|
+
# Sandbox is already paused
|
|
482
|
+
return False
|
|
483
|
+
|
|
484
|
+
if res.status_code >= 300:
|
|
485
|
+
raise handle_api_exception(res)
|
|
486
|
+
|
|
487
|
+
# Check if res.parse is Error
|
|
488
|
+
if isinstance(res.parsed, Error):
|
|
489
|
+
raise SandboxException(f"{res.parsed.message}: Request failed")
|
|
490
|
+
|
|
491
|
+
return True
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Special step name for the finalization phase of template building.
|
|
3
|
+
This is the last step that runs after all user-defined instructions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
FINALIZE_STEP_NAME = "finalize"
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Special step name for the base image phase of template building.
|
|
10
|
+
This is the first step that sets up the base image.
|
|
11
|
+
"""
|
|
12
|
+
BASE_STEP_NAME = "base"
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
Stack trace depth for capturing caller information.
|
|
16
|
+
|
|
17
|
+
Depth levels:
|
|
18
|
+
1. TemplateClass
|
|
19
|
+
2. Caller method (e.g., copy(), from_image(), etc.)
|
|
20
|
+
|
|
21
|
+
This depth is used to determine the original caller's location
|
|
22
|
+
for stack traces.
|
|
23
|
+
"""
|
|
24
|
+
STACK_TRACE_DEPTH = 2
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
Default setting for whether to resolve symbolic links when copying files.
|
|
28
|
+
When False, symlinks are copied as symlinks rather than following them.
|
|
29
|
+
"""
|
|
30
|
+
RESOLVE_SYMLINKS = False
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
Default setting for whether to gzip files when copying them into the
|
|
34
|
+
template. When True, the upload archive is gzipped before being uploaded.
|
|
35
|
+
"""
|
|
36
|
+
GZIP = True
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
Default timeout (in seconds) for uploading the build-context archive to the
|
|
40
|
+
S3 presigned URL. Uploads of large archives can take far longer than the 60s
|
|
41
|
+
general API request timeout, so the upload uses a 1-hour default unless the
|
|
42
|
+
caller passes an explicit ``request_timeout``. This matches the JS SDK's
|
|
43
|
+
``FILE_UPLOAD_TIMEOUT_MS``.
|
|
44
|
+
"""
|
|
45
|
+
FILE_UPLOAD_TIMEOUT_SECONDS = 3600
|