vers-sdk 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vers_sdk-0.1.1/PKG-INFO +10 -0
- vers_sdk-0.1.1/README.md +81 -0
- vers_sdk-0.1.1/pyproject.toml +19 -0
- vers_sdk-0.1.1/setup.cfg +4 -0
- vers_sdk-0.1.1/src/vers_sdk/__init__.py +34 -0
- vers_sdk-0.1.1/src/vers_sdk/client.py +529 -0
- vers_sdk-0.1.1/src/vers_sdk/errors.py +132 -0
- vers_sdk-0.1.1/src/vers_sdk/models.py +545 -0
- vers_sdk-0.1.1/src/vers_sdk.egg-info/PKG-INFO +10 -0
- vers_sdk-0.1.1/src/vers_sdk.egg-info/SOURCES.txt +12 -0
- vers_sdk-0.1.1/src/vers_sdk.egg-info/dependency_links.txt +1 -0
- vers_sdk-0.1.1/src/vers_sdk.egg-info/requires.txt +1 -0
- vers_sdk-0.1.1/src/vers_sdk.egg-info/top_level.txt +1 -0
- vers_sdk-0.1.1/tests/test_client.py +679 -0
vers_sdk-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vers-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: SDK for Orchestrator Control Plane API
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/hdresearch/python-sdk
|
|
7
|
+
Project-URL: Repository, https://github.com/hdresearch/python-sdk
|
|
8
|
+
Keywords: vers,sdk,api,vm,orchestration
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: httpx>=0.24
|
vers_sdk-0.1.1/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Vers Python API Library
|
|
2
|
+
|
|
3
|
+
This library provides convenient access to the [Vers](https://hdr.is) REST API from Python.
|
|
4
|
+
|
|
5
|
+
It is generated with [Sterling](https://github.com/hdresearch/sterling).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pip install vers-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import asyncio
|
|
17
|
+
from vers_sdk import VersSdkClient
|
|
18
|
+
|
|
19
|
+
async def main():
|
|
20
|
+
client = VersSdkClient(
|
|
21
|
+
api_key="your-api-key", # or set VERS_API_KEY env var
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# List all VMs
|
|
25
|
+
response = await client.list_vms()
|
|
26
|
+
print(response.json())
|
|
27
|
+
|
|
28
|
+
# Create a new root VM
|
|
29
|
+
vm = await client.create_new_root_vm(body={"vm_config": {}})
|
|
30
|
+
print(vm.json())
|
|
31
|
+
|
|
32
|
+
await client.close()
|
|
33
|
+
|
|
34
|
+
asyncio.run(main())
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
client = VersSdkClient(
|
|
41
|
+
api_key="your-api-key", # or set VERS_API_KEY env var
|
|
42
|
+
base_url="https://api.vers.sh", # or set VERS_BASE_URL env var
|
|
43
|
+
max_retries=2,
|
|
44
|
+
timeout=30.0,
|
|
45
|
+
http_client=custom_httpx_client, # optional custom httpx.AsyncClient
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Error handling
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from vers_sdk.errors import APIError, NotFoundError, RateLimitError
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
await client.delete_vm("nonexistent-id")
|
|
56
|
+
except NotFoundError as e:
|
|
57
|
+
print(f"Not found: {e.status} {e.message}")
|
|
58
|
+
except APIError as e:
|
|
59
|
+
print(f"API error: {e.status}")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Retries
|
|
63
|
+
|
|
64
|
+
Requests are automatically retried up to 2 times on 5xx errors and connection failures,
|
|
65
|
+
with exponential backoff and jitter. The client respects `Retry-After` headers (capped at 60s).
|
|
66
|
+
|
|
67
|
+
## Async context manager
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
async with VersSdkClient(api_key="...") as client:
|
|
71
|
+
vms = await client.list_vms()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Requirements
|
|
75
|
+
|
|
76
|
+
- Python 3.10+
|
|
77
|
+
- httpx
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "vers-sdk"
|
|
3
|
+
version = "0.1.1"
|
|
4
|
+
description = "SDK for Orchestrator Control Plane API"
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
keywords = ["vers", "sdk", "api", "vm", "orchestration"]
|
|
8
|
+
dependencies = ["httpx>=0.24"]
|
|
9
|
+
|
|
10
|
+
[project.urls]
|
|
11
|
+
Homepage = "https://github.com/hdresearch/python-sdk"
|
|
12
|
+
Repository = "https://github.com/hdresearch/python-sdk"
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["setuptools>=68.0"]
|
|
16
|
+
build-backend = "setuptools.build_meta"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["src"]
|
vers_sdk-0.1.1/setup.cfg
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Generated by Sterling SDK Generator
|
|
2
|
+
# Orchestrator Control Plane API v0.1.0
|
|
3
|
+
|
|
4
|
+
from .client import VersSdkClient
|
|
5
|
+
from .errors import (
|
|
6
|
+
VersSDKError,
|
|
7
|
+
APIError,
|
|
8
|
+
APIConnectionError,
|
|
9
|
+
APIConnectionTimeoutError,
|
|
10
|
+
BadRequestError,
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
PermissionDeniedError,
|
|
13
|
+
NotFoundError,
|
|
14
|
+
ConflictError,
|
|
15
|
+
UnprocessableEntityError,
|
|
16
|
+
RateLimitError,
|
|
17
|
+
InternalServerError,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"VersSdkClient",
|
|
22
|
+
"VersSDKError",
|
|
23
|
+
"APIError",
|
|
24
|
+
"APIConnectionError",
|
|
25
|
+
"APIConnectionTimeoutError",
|
|
26
|
+
"BadRequestError",
|
|
27
|
+
"AuthenticationError",
|
|
28
|
+
"PermissionDeniedError",
|
|
29
|
+
"NotFoundError",
|
|
30
|
+
"ConflictError",
|
|
31
|
+
"UnprocessableEntityError",
|
|
32
|
+
"RateLimitError",
|
|
33
|
+
"InternalServerError",
|
|
34
|
+
]
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
# Generated by Sterling SDK Generator
|
|
2
|
+
# Orchestrator Control Plane API v0.1.0
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import calendar
|
|
6
|
+
import email.utils
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import random
|
|
10
|
+
import time as _time
|
|
11
|
+
import uuid
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
|
|
15
|
+
import httpx
|
|
16
|
+
|
|
17
|
+
from .errors import APIError, APIConnectionError
|
|
18
|
+
from .models import * # noqa: F403
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class RequestOptions:
|
|
23
|
+
"""Per-request options that override client defaults."""
|
|
24
|
+
|
|
25
|
+
#: Additional headers to send with the request.
|
|
26
|
+
#: Set a header to None to remove it.
|
|
27
|
+
headers: dict[str, str | None] | None = None
|
|
28
|
+
|
|
29
|
+
#: Request timeout in seconds. Overrides the client-level timeout.
|
|
30
|
+
timeout: float | None = None
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger("vers_sdk")
|
|
33
|
+
|
|
34
|
+
_LOG_LEVEL_MAP = {
|
|
35
|
+
"debug": logging.DEBUG,
|
|
36
|
+
"info": logging.INFO,
|
|
37
|
+
"warn": logging.WARNING,
|
|
38
|
+
"error": logging.ERROR,
|
|
39
|
+
"off": logging.CRITICAL + 1,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _configure_logger() -> None:
|
|
44
|
+
env_level = os.environ.get("VERS_LOG", "warn").lower()
|
|
45
|
+
level = _LOG_LEVEL_MAP.get(env_level, logging.WARNING)
|
|
46
|
+
logger.setLevel(level)
|
|
47
|
+
if not logger.handlers:
|
|
48
|
+
handler = logging.StreamHandler()
|
|
49
|
+
handler.setFormatter(logging.Formatter("%(asctime)s [vers_sdk %(levelname)s] %(message)s"))
|
|
50
|
+
logger.addHandler(handler)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
_configure_logger()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Status codes that are retryable even though they are 4xx
|
|
57
|
+
RETRYABLE_STATUS_CODES = {408, 409, 429}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _is_retryable_status(status: int) -> bool:
|
|
61
|
+
return status >= 500 or status in RETRYABLE_STATUS_CODES
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _retry_delay(attempt: int) -> float:
|
|
65
|
+
base = 0.5 * (2 ** attempt)
|
|
66
|
+
jitter = random.random() * base * 0.25
|
|
67
|
+
return base + jitter
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _parse_retry_after(header_value: Optional[str]) -> Optional[float]:
|
|
71
|
+
if header_value is None:
|
|
72
|
+
return None
|
|
73
|
+
try:
|
|
74
|
+
delay = float(header_value)
|
|
75
|
+
return min(max(delay, 0.0), 60.0)
|
|
76
|
+
except (ValueError, OverflowError):
|
|
77
|
+
pass
|
|
78
|
+
parsed = email.utils.parsedate(header_value)
|
|
79
|
+
if parsed is not None:
|
|
80
|
+
retry_time = calendar.timegm(parsed)
|
|
81
|
+
delay = retry_time - _time.time()
|
|
82
|
+
return min(max(delay, 0.0), 60.0)
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class VersSdkClient:
|
|
87
|
+
"""Client for Orchestrator Control Plane API"""
|
|
88
|
+
|
|
89
|
+
def __init__(
|
|
90
|
+
self,
|
|
91
|
+
base_url: Optional[str] = None,
|
|
92
|
+
api_key: Optional[str] = None,
|
|
93
|
+
headers: Optional[dict[str, str | None]] = None,
|
|
94
|
+
max_retries: int = 2,
|
|
95
|
+
timeout: float = 30.0,
|
|
96
|
+
http_client: Optional[httpx.AsyncClient] = None,
|
|
97
|
+
):
|
|
98
|
+
self.base_url = base_url or os.environ.get("VERS_BASE_URL", "https://api.vers.sh")
|
|
99
|
+
self.api_key = api_key or os.environ.get("VERS_API_KEY")
|
|
100
|
+
self.max_retries = max_retries
|
|
101
|
+
self.timeout = timeout
|
|
102
|
+
self._custom_headers: dict[str, str | None] = headers or {}
|
|
103
|
+
if http_client is not None:
|
|
104
|
+
self._client = http_client
|
|
105
|
+
else:
|
|
106
|
+
import platform as _platform
|
|
107
|
+
default_headers: dict[str, str] = {
|
|
108
|
+
"User-Agent": f"vers-sdk/0.1.1 python/{_platform.python_version()} {_platform.system().lower()}/{_platform.machine()}",
|
|
109
|
+
}
|
|
110
|
+
if self.api_key:
|
|
111
|
+
default_headers["Authorization"] = f"Bearer {self.api_key}"
|
|
112
|
+
merged = self._merge_headers(default_headers, self._custom_headers)
|
|
113
|
+
self._client = httpx.AsyncClient(
|
|
114
|
+
base_url=self.base_url,
|
|
115
|
+
headers=merged,
|
|
116
|
+
timeout=self.timeout,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _merge_headers(*sources: Optional[dict[str, str | None]]) -> dict[str, str]:
|
|
121
|
+
"""Merge header dicts. Later sources override earlier ones (case-insensitive).
|
|
122
|
+
A ``None`` value removes the header."""
|
|
123
|
+
merged: dict[str, str] = {}
|
|
124
|
+
for source in sources:
|
|
125
|
+
if source is None:
|
|
126
|
+
continue
|
|
127
|
+
for key, value in source.items():
|
|
128
|
+
if value is None:
|
|
129
|
+
# Remove header (case-insensitive)
|
|
130
|
+
merged = {
|
|
131
|
+
k: v for k, v in merged.items()
|
|
132
|
+
if k.lower() != key.lower()
|
|
133
|
+
}
|
|
134
|
+
else:
|
|
135
|
+
merged[key] = value
|
|
136
|
+
return merged
|
|
137
|
+
|
|
138
|
+
def _check_response(self, response: httpx.Response) -> None:
|
|
139
|
+
if response.is_success:
|
|
140
|
+
return
|
|
141
|
+
body: Any = None
|
|
142
|
+
message: str | None = None
|
|
143
|
+
try:
|
|
144
|
+
body = response.json()
|
|
145
|
+
except Exception:
|
|
146
|
+
message = response.text
|
|
147
|
+
headers = dict(response.headers)
|
|
148
|
+
raise APIError.generate(
|
|
149
|
+
status=response.status_code,
|
|
150
|
+
body=body,
|
|
151
|
+
message=message,
|
|
152
|
+
headers=headers,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
async def _request(self, method: str, path: str, options: RequestOptions | None = None, **kwargs: Any) -> httpx.Response:
|
|
156
|
+
logger.debug("request: %s %s", method, path)
|
|
157
|
+
|
|
158
|
+
req_headers: dict[str, str] = kwargs.pop("headers", {})
|
|
159
|
+
if method.upper() in ("POST", "PUT", "PATCH", "DELETE"):
|
|
160
|
+
req_headers["X-Idempotency-Key"] = str(uuid.uuid4())
|
|
161
|
+
# Apply per-request header overrides
|
|
162
|
+
if options and options.headers:
|
|
163
|
+
for k, v in options.headers.items():
|
|
164
|
+
if v is None:
|
|
165
|
+
req_headers.pop(k, None)
|
|
166
|
+
else:
|
|
167
|
+
req_headers[k] = v
|
|
168
|
+
if req_headers:
|
|
169
|
+
kwargs["headers"] = req_headers
|
|
170
|
+
effective_timeout = options.timeout if options and options.timeout is not None else None
|
|
171
|
+
|
|
172
|
+
last_exc: Optional[Exception] = None
|
|
173
|
+
retry_after_used = False
|
|
174
|
+
for attempt in range(self.max_retries + 1):
|
|
175
|
+
if attempt > 0 and not retry_after_used:
|
|
176
|
+
logger.info("retry attempt %d/%d for %s %s", attempt, self.max_retries, method, path)
|
|
177
|
+
await asyncio.sleep(_retry_delay(attempt - 1))
|
|
178
|
+
retry_after_used = False
|
|
179
|
+
try:
|
|
180
|
+
req_kwargs = dict(kwargs)
|
|
181
|
+
if effective_timeout is not None:
|
|
182
|
+
req_kwargs["timeout"] = effective_timeout
|
|
183
|
+
response = await self._client.request(method, path, **req_kwargs)
|
|
184
|
+
# Remove headers that were set to None
|
|
185
|
+
logger.debug("response: %d for %s %s", response.status_code, method, path)
|
|
186
|
+
if _is_retryable_status(response.status_code) and attempt < self.max_retries:
|
|
187
|
+
ra_delay = _parse_retry_after(response.headers.get("retry-after"))
|
|
188
|
+
if ra_delay is not None:
|
|
189
|
+
retry_after_used = True
|
|
190
|
+
logger.info("retry attempt %d/%d for %s %s (retry-after: %.1fs)", attempt + 1, self.max_retries, method, path, ra_delay)
|
|
191
|
+
await asyncio.sleep(ra_delay)
|
|
192
|
+
last_exc = Exception(f"HTTP {response.status_code}")
|
|
193
|
+
continue
|
|
194
|
+
self._check_response(response)
|
|
195
|
+
return response
|
|
196
|
+
except APIError:
|
|
197
|
+
raise
|
|
198
|
+
except (httpx.ConnectError, httpx.TimeoutException) as exc:
|
|
199
|
+
logger.error("connection error: %s %s - %s", method, path, exc)
|
|
200
|
+
if attempt == self.max_retries:
|
|
201
|
+
raise APIConnectionError(cause=exc) from exc
|
|
202
|
+
last_exc = exc
|
|
203
|
+
raise last_exc # type: ignore[misc]
|
|
204
|
+
|
|
205
|
+
async def resize_vm_disk(self, vm_id: str, body: dict[str, Any], params: ResizeVmDiskParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
206
|
+
""""""
|
|
207
|
+
path = f"/api/v1/vm/{vm_id}/disk"
|
|
208
|
+
query: dict[str, Any] = {}
|
|
209
|
+
if params is not None:
|
|
210
|
+
if params.skip_wait_boot is not None:
|
|
211
|
+
query["skip_wait_boot"] = params.skip_wait_boot
|
|
212
|
+
return await self._request("PATCH", path, options, json=body, params=query)
|
|
213
|
+
|
|
214
|
+
async def exec_vm_stream_attach(self, vm_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
215
|
+
""""""
|
|
216
|
+
path = f"/api/v1/vm/{vm_id}/exec/stream/attach"
|
|
217
|
+
return await self._request("POST", path, options, json=body)
|
|
218
|
+
|
|
219
|
+
async def create_new_root_vm(self, body: dict[str, Any], params: CreateNewRootVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
220
|
+
""""""
|
|
221
|
+
path = "/api/v1/vm/new_root"
|
|
222
|
+
query: dict[str, Any] = {}
|
|
223
|
+
if params is not None:
|
|
224
|
+
if params.wait_boot is not None:
|
|
225
|
+
query["wait_boot"] = params.wait_boot
|
|
226
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
227
|
+
|
|
228
|
+
async def vm_logs(self, vm_id: str, params: VmLogsParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
229
|
+
""""""
|
|
230
|
+
path = f"/api/v1/vm/{vm_id}/logs"
|
|
231
|
+
query: dict[str, Any] = {}
|
|
232
|
+
if params is not None:
|
|
233
|
+
if params.offset is not None:
|
|
234
|
+
query["offset"] = params.offset
|
|
235
|
+
if params.max_entries is not None:
|
|
236
|
+
query["max_entries"] = params.max_entries
|
|
237
|
+
if params.stream is not None:
|
|
238
|
+
query["stream"] = params.stream
|
|
239
|
+
return await self._request("GET", path, options, params=query)
|
|
240
|
+
|
|
241
|
+
async def branch_by_ref(self, repo_name: str, tag_name: str, body: dict[str, Any] | None = None, params: BranchByRefParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
242
|
+
""""""
|
|
243
|
+
path = f"/api/v1/vm/branch/by_ref/{repo_name}/{tag_name}"
|
|
244
|
+
query: dict[str, Any] = {}
|
|
245
|
+
if params is not None:
|
|
246
|
+
if params.count is not None:
|
|
247
|
+
query["count"] = params.count
|
|
248
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
249
|
+
|
|
250
|
+
async def list_public_commits(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
251
|
+
""""""
|
|
252
|
+
path = "/api/v1/commits/public"
|
|
253
|
+
return await self._request("GET", path, options)
|
|
254
|
+
|
|
255
|
+
async def list_public_repo_tags(self, org_name: str, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
256
|
+
""""""
|
|
257
|
+
path = f"/api/v1/public/repositories/{org_name}/{repo_name}/tags"
|
|
258
|
+
return await self._request("GET", path, options)
|
|
259
|
+
|
|
260
|
+
async def branch_by_tag(self, tag_name: str, body: dict[str, Any] | None = None, params: BranchByTagParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
261
|
+
""""""
|
|
262
|
+
path = f"/api/v1/vm/branch/by_tag/{tag_name}"
|
|
263
|
+
query: dict[str, Any] = {}
|
|
264
|
+
if params is not None:
|
|
265
|
+
if params.count is not None:
|
|
266
|
+
query["count"] = params.count
|
|
267
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
268
|
+
|
|
269
|
+
async def branch_vm(self, vm_or_commit_id: str, body: dict[str, Any] | None = None, params: BranchVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
270
|
+
""""""
|
|
271
|
+
path = f"/api/v1/vm/{vm_or_commit_id}/branch"
|
|
272
|
+
query: dict[str, Any] = {}
|
|
273
|
+
if params is not None:
|
|
274
|
+
if params.keep_paused is not None:
|
|
275
|
+
query["keep_paused"] = params.keep_paused
|
|
276
|
+
if params.skip_wait_boot is not None:
|
|
277
|
+
query["skip_wait_boot"] = params.skip_wait_boot
|
|
278
|
+
if params.count is not None:
|
|
279
|
+
query["count"] = params.count
|
|
280
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
281
|
+
|
|
282
|
+
async def get_repo_tag(self, repo_name: str, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
283
|
+
""""""
|
|
284
|
+
path = f"/api/v1/repositories/{repo_name}/tags/{tag_name}"
|
|
285
|
+
return await self._request("GET", path, options)
|
|
286
|
+
|
|
287
|
+
async def delete_repo_tag(self, repo_name: str, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
288
|
+
""""""
|
|
289
|
+
path = f"/api/v1/repositories/{repo_name}/tags/{tag_name}"
|
|
290
|
+
return await self._request("DELETE", path, options)
|
|
291
|
+
|
|
292
|
+
async def update_repo_tag(self, repo_name: str, tag_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
293
|
+
""""""
|
|
294
|
+
path = f"/api/v1/repositories/{repo_name}/tags/{tag_name}"
|
|
295
|
+
return await self._request("PATCH", path, options, json=body)
|
|
296
|
+
|
|
297
|
+
async def get_repository(self, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
298
|
+
""""""
|
|
299
|
+
path = f"/api/v1/repositories/{repo_name}"
|
|
300
|
+
return await self._request("GET", path, options)
|
|
301
|
+
|
|
302
|
+
async def delete_repository(self, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
303
|
+
""""""
|
|
304
|
+
path = f"/api/v1/repositories/{repo_name}"
|
|
305
|
+
return await self._request("DELETE", path, options)
|
|
306
|
+
|
|
307
|
+
async def list_parent_commits(self, commit_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
308
|
+
""""""
|
|
309
|
+
path = f"/api/v1/vm/commits/{commit_id}/parents"
|
|
310
|
+
return await self._request("GET", path, options)
|
|
311
|
+
|
|
312
|
+
async def update_vm_state(self, vm_id: str, body: dict[str, Any], params: UpdateVmStateParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
313
|
+
""""""
|
|
314
|
+
path = f"/api/v1/vm/{vm_id}/state"
|
|
315
|
+
query: dict[str, Any] = {}
|
|
316
|
+
if params is not None:
|
|
317
|
+
if params.skip_wait_boot is not None:
|
|
318
|
+
query["skip_wait_boot"] = params.skip_wait_boot
|
|
319
|
+
return await self._request("PATCH", path, options, json=body, params=query)
|
|
320
|
+
|
|
321
|
+
async def fork_repository(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
322
|
+
""""""
|
|
323
|
+
path = "/api/v1/repositories/fork"
|
|
324
|
+
return await self._request("POST", path, options, json=body)
|
|
325
|
+
|
|
326
|
+
async def list_repositories(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
327
|
+
""""""
|
|
328
|
+
path = "/api/v1/repositories"
|
|
329
|
+
return await self._request("GET", path, options)
|
|
330
|
+
|
|
331
|
+
async def create_repository(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
332
|
+
""""""
|
|
333
|
+
path = "/api/v1/repositories"
|
|
334
|
+
return await self._request("POST", path, options, json=body)
|
|
335
|
+
|
|
336
|
+
async def restore_from_commit(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
337
|
+
""""""
|
|
338
|
+
path = "/api/v1/vm/from_commit"
|
|
339
|
+
return await self._request("POST", path, options, json=body)
|
|
340
|
+
|
|
341
|
+
async def get_domain(self, domain_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
342
|
+
""""""
|
|
343
|
+
path = f"/api/v1/domains/{domain_id}"
|
|
344
|
+
return await self._request("GET", path, options)
|
|
345
|
+
|
|
346
|
+
async def delete_domain(self, domain_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
347
|
+
""""""
|
|
348
|
+
path = f"/api/v1/domains/{domain_id}"
|
|
349
|
+
return await self._request("DELETE", path, options)
|
|
350
|
+
|
|
351
|
+
async def list_tags(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
352
|
+
""""""
|
|
353
|
+
path = "/api/v1/commit_tags"
|
|
354
|
+
return await self._request("GET", path, options)
|
|
355
|
+
|
|
356
|
+
async def create_tag(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
357
|
+
""""""
|
|
358
|
+
path = "/api/v1/commit_tags"
|
|
359
|
+
return await self._request("POST", path, options, json=body)
|
|
360
|
+
|
|
361
|
+
async def list_public_repositories(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
362
|
+
""""""
|
|
363
|
+
path = "/api/v1/public/repositories"
|
|
364
|
+
return await self._request("GET", path, options)
|
|
365
|
+
|
|
366
|
+
async def get_public_repo_tag(self, org_name: str, repo_name: str, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
367
|
+
""""""
|
|
368
|
+
path = f"/api/v1/public/repositories/{org_name}/{repo_name}/tags/{tag_name}"
|
|
369
|
+
return await self._request("GET", path, options)
|
|
370
|
+
|
|
371
|
+
async def get_vm_metadata(self, vm_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
372
|
+
""""""
|
|
373
|
+
path = f"/api/v1/vm/{vm_id}/metadata"
|
|
374
|
+
return await self._request("GET", path, options)
|
|
375
|
+
|
|
376
|
+
async def list_env_vars(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
377
|
+
""""""
|
|
378
|
+
path = "/api/v1/env_vars"
|
|
379
|
+
return await self._request("GET", path, options)
|
|
380
|
+
|
|
381
|
+
async def set_env_vars(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
382
|
+
""""""
|
|
383
|
+
path = "/api/v1/env_vars"
|
|
384
|
+
return await self._request("PUT", path, options, json=body)
|
|
385
|
+
|
|
386
|
+
async def delete_vm(self, vm_id: str, params: DeleteVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
387
|
+
""""""
|
|
388
|
+
path = f"/api/v1/vm/{vm_id}"
|
|
389
|
+
query: dict[str, Any] = {}
|
|
390
|
+
if params is not None:
|
|
391
|
+
if params.skip_wait_boot is not None:
|
|
392
|
+
query["skip_wait_boot"] = params.skip_wait_boot
|
|
393
|
+
return await self._request("DELETE", path, options, params=query)
|
|
394
|
+
|
|
395
|
+
async def list_commits(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
396
|
+
""""""
|
|
397
|
+
path = "/api/v1/commits"
|
|
398
|
+
return await self._request("GET", path, options)
|
|
399
|
+
|
|
400
|
+
async def get_tag(self, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
401
|
+
""""""
|
|
402
|
+
path = f"/api/v1/commit_tags/{tag_name}"
|
|
403
|
+
return await self._request("GET", path, options)
|
|
404
|
+
|
|
405
|
+
async def delete_tag(self, tag_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
406
|
+
""""""
|
|
407
|
+
path = f"/api/v1/commit_tags/{tag_name}"
|
|
408
|
+
return await self._request("DELETE", path, options)
|
|
409
|
+
|
|
410
|
+
async def update_tag(self, tag_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
411
|
+
""""""
|
|
412
|
+
path = f"/api/v1/commit_tags/{tag_name}"
|
|
413
|
+
return await self._request("PATCH", path, options, json=body)
|
|
414
|
+
|
|
415
|
+
async def branch_by_vm(self, vm_id: str, body: dict[str, Any] | None = None, params: BranchByVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
416
|
+
""""""
|
|
417
|
+
path = f"/api/v1/vm/branch/by_vm/{vm_id}"
|
|
418
|
+
query: dict[str, Any] = {}
|
|
419
|
+
if params is not None:
|
|
420
|
+
if params.keep_paused is not None:
|
|
421
|
+
query["keep_paused"] = params.keep_paused
|
|
422
|
+
if params.skip_wait_boot is not None:
|
|
423
|
+
query["skip_wait_boot"] = params.skip_wait_boot
|
|
424
|
+
if params.count is not None:
|
|
425
|
+
query["count"] = params.count
|
|
426
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
427
|
+
|
|
428
|
+
async def delete_env_var(self, key: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
429
|
+
""""""
|
|
430
|
+
path = f"/api/v1/env_vars/{key}"
|
|
431
|
+
return await self._request("DELETE", path, options)
|
|
432
|
+
|
|
433
|
+
async def exec_vm_stream(self, vm_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
434
|
+
""""""
|
|
435
|
+
path = f"/api/v1/vm/{vm_id}/exec/stream"
|
|
436
|
+
return await self._request("POST", path, options, json=body)
|
|
437
|
+
|
|
438
|
+
async def commit_vm(self, vm_id: str, body: dict[str, Any] | None = None, params: CommitVmParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
439
|
+
""""""
|
|
440
|
+
path = f"/api/v1/vm/{vm_id}/commit"
|
|
441
|
+
query: dict[str, Any] = {}
|
|
442
|
+
if params is not None:
|
|
443
|
+
if params.keep_paused is not None:
|
|
444
|
+
query["keep_paused"] = params.keep_paused
|
|
445
|
+
if params.skip_wait_boot is not None:
|
|
446
|
+
query["skip_wait_boot"] = params.skip_wait_boot
|
|
447
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
448
|
+
|
|
449
|
+
async def delete_commit(self, commit_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
450
|
+
""""""
|
|
451
|
+
path = f"/api/v1/commits/{commit_id}"
|
|
452
|
+
return await self._request("DELETE", path, options)
|
|
453
|
+
|
|
454
|
+
async def update_commit(self, commit_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
455
|
+
""""""
|
|
456
|
+
path = f"/api/v1/commits/{commit_id}"
|
|
457
|
+
return await self._request("PATCH", path, options, json=body)
|
|
458
|
+
|
|
459
|
+
async def list_repo_tags(self, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
460
|
+
""""""
|
|
461
|
+
path = f"/api/v1/repositories/{repo_name}/tags"
|
|
462
|
+
return await self._request("GET", path, options)
|
|
463
|
+
|
|
464
|
+
async def create_repo_tag(self, repo_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
465
|
+
""""""
|
|
466
|
+
path = f"/api/v1/repositories/{repo_name}/tags"
|
|
467
|
+
return await self._request("POST", path, options, json=body)
|
|
468
|
+
|
|
469
|
+
async def ssh_key(self, vm_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
470
|
+
""""""
|
|
471
|
+
path = f"/api/v1/vm/{vm_id}/ssh_key"
|
|
472
|
+
return await self._request("GET", path, options)
|
|
473
|
+
|
|
474
|
+
async def exec_vm(self, vm_id: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
475
|
+
""""""
|
|
476
|
+
path = f"/api/v1/vm/{vm_id}/exec"
|
|
477
|
+
return await self._request("POST", path, options, json=body)
|
|
478
|
+
|
|
479
|
+
async def set_repository_visibility(self, repo_name: str, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
480
|
+
""""""
|
|
481
|
+
path = f"/api/v1/repositories/{repo_name}/visibility"
|
|
482
|
+
return await self._request("PATCH", path, options, json=body)
|
|
483
|
+
|
|
484
|
+
async def list_vms(self, options: RequestOptions | None = None) -> httpx.Response:
|
|
485
|
+
""""""
|
|
486
|
+
path = "/api/v1/vms"
|
|
487
|
+
return await self._request("GET", path, options)
|
|
488
|
+
|
|
489
|
+
async def list_domains(self, params: ListDomainsParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
490
|
+
""""""
|
|
491
|
+
path = "/api/v1/domains"
|
|
492
|
+
query: dict[str, Any] = {}
|
|
493
|
+
if params is not None:
|
|
494
|
+
if params.vm_id is not None:
|
|
495
|
+
query["vm_id"] = params.vm_id
|
|
496
|
+
return await self._request("GET", path, options, params=query)
|
|
497
|
+
|
|
498
|
+
async def create_domain(self, body: dict[str, Any], options: RequestOptions | None = None) -> httpx.Response:
|
|
499
|
+
""""""
|
|
500
|
+
path = "/api/v1/domains"
|
|
501
|
+
return await self._request("POST", path, options, json=body)
|
|
502
|
+
|
|
503
|
+
async def vm_status(self, vm_id: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
504
|
+
""""""
|
|
505
|
+
path = f"/api/v1/vm/{vm_id}/status"
|
|
506
|
+
return await self._request("GET", path, options)
|
|
507
|
+
|
|
508
|
+
async def get_public_repository(self, org_name: str, repo_name: str, options: RequestOptions | None = None) -> httpx.Response:
|
|
509
|
+
""""""
|
|
510
|
+
path = f"/api/v1/public/repositories/{org_name}/{repo_name}"
|
|
511
|
+
return await self._request("GET", path, options)
|
|
512
|
+
|
|
513
|
+
async def branch_by_commit(self, commit_id: str, body: dict[str, Any] | None = None, params: BranchByCommitParams | None = None, options: RequestOptions | None = None) -> httpx.Response:
|
|
514
|
+
""""""
|
|
515
|
+
path = f"/api/v1/vm/branch/by_commit/{commit_id}"
|
|
516
|
+
query: dict[str, Any] = {}
|
|
517
|
+
if params is not None:
|
|
518
|
+
if params.count is not None:
|
|
519
|
+
query["count"] = params.count
|
|
520
|
+
return await self._request("POST", path, options, json=body, params=query)
|
|
521
|
+
|
|
522
|
+
async def close(self) -> None:
|
|
523
|
+
await self._client.aclose()
|
|
524
|
+
|
|
525
|
+
async def __aenter__(self) -> "VersSdkClient":
|
|
526
|
+
return self
|
|
527
|
+
|
|
528
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
529
|
+
await self.close()
|