drukbox-python-sdk 0.2.0__py3-none-any.whl → 0.3.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.
- {drukbox_python_sdk-0.2.0.dist-info → drukbox_python_sdk-0.3.0.dist-info}/METADATA +23 -2
- drukbox_python_sdk-0.3.0.dist-info/RECORD +7 -0
- drukbox_sdk/__init__.py +2 -0
- drukbox_sdk/api.py +47 -0
- drukbox_python_sdk-0.2.0.dist-info/RECORD +0 -7
- {drukbox_python_sdk-0.2.0.dist-info → drukbox_python_sdk-0.3.0.dist-info}/WHEEL +0 -0
- {drukbox_python_sdk-0.2.0.dist-info → drukbox_python_sdk-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: drukbox-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Async Python client for Drukbox.
|
|
5
5
|
Project-URL: Homepage, https://github.com/clawhaven/drukbox-python-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/clawhaven/drukbox-python-sdk
|
|
@@ -73,6 +73,7 @@ Public exports live in `drukbox_sdk`:
|
|
|
73
73
|
- `SandboxTemplate`
|
|
74
74
|
- `Secret`
|
|
75
75
|
- `Issuer`
|
|
76
|
+
- `ServiceAccount`
|
|
76
77
|
- `DoctorReport` and `DoctorCheck`
|
|
77
78
|
- `HTTPProxy` and `HTTPProxyAttachment`
|
|
78
79
|
- `SandboxAPIError` and typed subclasses for auth, not found, conflict,
|
|
@@ -86,6 +87,7 @@ Supported host operations:
|
|
|
86
87
|
- `list_hosts`
|
|
87
88
|
- `renew_host`
|
|
88
89
|
- `delete_host`
|
|
90
|
+
- `refresh_secret`
|
|
89
91
|
- `doctor`
|
|
90
92
|
- `aclose`
|
|
91
93
|
|
|
@@ -127,6 +129,24 @@ The response carries no secret. A service name Drukbox does not know raises
|
|
|
127
129
|
`expires_at` to extend by the service's default TTL; renewal never makes a host
|
|
128
130
|
permanent.
|
|
129
131
|
|
|
132
|
+
`SandboxHost.service_account` names the service account that created or
|
|
133
|
+
claimed the host, `admin` for an admin key, or `None` for an unclaimed warm
|
|
134
|
+
host.
|
|
135
|
+
|
|
136
|
+
`refresh_secret` makes the service forget the current value of one secret on
|
|
137
|
+
a host and fetch a new one from its issuer. A static secret raises
|
|
138
|
+
`SandboxConflictError`; an issuer or exchange that did not answer raises
|
|
139
|
+
`SandboxUnavailableError`.
|
|
140
|
+
|
|
141
|
+
Supported service account operations, admin key only:
|
|
142
|
+
|
|
143
|
+
- `create_service_account`
|
|
144
|
+
- `remove_service_account`
|
|
145
|
+
|
|
146
|
+
`create_service_account` returns a `ServiceAccount` with the token the
|
|
147
|
+
service hands out once. Store it. `remove_service_account` revokes it on the
|
|
148
|
+
next request.
|
|
149
|
+
|
|
130
150
|
Supported template operations:
|
|
131
151
|
|
|
132
152
|
- `create_template`
|
|
@@ -153,7 +173,8 @@ proxy at a host's backing VM (the host must be `bootstrapping` or `active`).
|
|
|
153
173
|
|
|
154
174
|
`doctor` fetches `GET /doctor` — read-only dependency health. The service
|
|
155
175
|
runs one cheap, non-mutating probe per dependency (database, active VM
|
|
156
|
-
provider, Tailscale when enabled) and always responds 200,
|
|
176
|
+
provider, secrets exchange, Tailscale when enabled) and always responds 200,
|
|
177
|
+
so callers branch
|
|
157
178
|
on `DoctorReport.ok` rather than the HTTP status. A failed `DoctorCheck`
|
|
158
179
|
carries a stable `hint` slug for remediation.
|
|
159
180
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
drukbox_sdk/__init__.py,sha256=bU2rOTzC3GMXSpiXvoMVwFOuKJUPZWSXhnteaRFxT_Y,1012
|
|
2
|
+
drukbox_sdk/api.py,sha256=eoXXnK8mkrSK9Q4tRSuVbN-6mSjbBTTeEsH_jF3U_wY,24118
|
|
3
|
+
drukbox_sdk/exceptions.py,sha256=Vzs4zVOywWsWfiZm6vgQ65ScI3Nn7zLGVj-8eT1YAR0,2224
|
|
4
|
+
drukbox_python_sdk-0.3.0.dist-info/METADATA,sha256=KL-_BzJPSEVgimNohBs8BIxhwcU8dreFTdVSihBlUaI,6599
|
|
5
|
+
drukbox_python_sdk-0.3.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
6
|
+
drukbox_python_sdk-0.3.0.dist-info/licenses/LICENSE,sha256=zM66rWL3BWGJBa_mZPnM0WUomSSZ1xtWVtGQovQ71M8,1078
|
|
7
|
+
drukbox_python_sdk-0.3.0.dist-info/RECORD,,
|
drukbox_sdk/__init__.py
CHANGED
|
@@ -14,6 +14,7 @@ from .api import (
|
|
|
14
14
|
SandboxHost,
|
|
15
15
|
SandboxTemplate,
|
|
16
16
|
Secret,
|
|
17
|
+
ServiceAccount,
|
|
17
18
|
)
|
|
18
19
|
from .exceptions import (
|
|
19
20
|
SandboxAPIError,
|
|
@@ -44,4 +45,5 @@ __all__ = [
|
|
|
44
45
|
"SandboxUnavailableError",
|
|
45
46
|
"SandboxValidationError",
|
|
46
47
|
"Secret",
|
|
48
|
+
"ServiceAccount",
|
|
47
49
|
]
|
drukbox_sdk/api.py
CHANGED
|
@@ -85,10 +85,15 @@ class SandboxHost:
|
|
|
85
85
|
different SSH auth model (exe.dev's edge proxy, Tailscale ACLs
|
|
86
86
|
via tailscaled-SSH) return None here. A later ``get_host`` call
|
|
87
87
|
always returns None — the key is not persisted server-side.
|
|
88
|
+
|
|
89
|
+
``service_account`` names the service account that created or claimed
|
|
90
|
+
the host, ``"admin"`` for an admin key, or None for an unclaimed warm
|
|
91
|
+
host.
|
|
88
92
|
"""
|
|
89
93
|
|
|
90
94
|
id: str
|
|
91
95
|
name: str
|
|
96
|
+
service_account: str | None
|
|
92
97
|
status: str
|
|
93
98
|
provider: str
|
|
94
99
|
image: str
|
|
@@ -196,6 +201,15 @@ class DoctorReport:
|
|
|
196
201
|
checks: list[DoctorCheck]
|
|
197
202
|
|
|
198
203
|
|
|
204
|
+
@dataclass(frozen=True)
|
|
205
|
+
class ServiceAccount:
|
|
206
|
+
"""A name the service knows a caller by, and the token that caller
|
|
207
|
+
presents. The service returns the token once, at creation."""
|
|
208
|
+
|
|
209
|
+
name: str
|
|
210
|
+
token: str = field(repr=False)
|
|
211
|
+
|
|
212
|
+
|
|
199
213
|
@dataclass(frozen=True)
|
|
200
214
|
class HTTPProxy:
|
|
201
215
|
"""An account-bound HTTP proxy as returned by the service.
|
|
@@ -401,6 +415,18 @@ class SandboxAPI:
|
|
|
401
415
|
async def delete_host(self, host_id: uuid.UUID | str) -> None:
|
|
402
416
|
await self._request("DELETE", f"/hosts/{host_id}")
|
|
403
417
|
|
|
418
|
+
async def refresh_secret(self, host_id: uuid.UUID | str, service: str) -> None:
|
|
419
|
+
"""Make the service forget the current value of ``service`` on the
|
|
420
|
+
host and fetch a new one from its issuer.
|
|
421
|
+
|
|
422
|
+
Raises :class:`SandboxNotFoundError` for an unknown host or secret,
|
|
423
|
+
:class:`SandboxConflictError` for a secret with a static value, and
|
|
424
|
+
:class:`SandboxUnavailableError` when the exchange or the issuer did
|
|
425
|
+
not answer.
|
|
426
|
+
"""
|
|
427
|
+
|
|
428
|
+
await self._request("POST", f"/hosts/{host_id}/secrets/{service}/refresh")
|
|
429
|
+
|
|
404
430
|
async def create_template(
|
|
405
431
|
self,
|
|
406
432
|
*,
|
|
@@ -466,6 +492,27 @@ class SandboxAPI:
|
|
|
466
492
|
checks=[DoctorCheck(**check) for check in data["checks"]],
|
|
467
493
|
)
|
|
468
494
|
|
|
495
|
+
async def create_service_account(self, name: str) -> ServiceAccount:
|
|
496
|
+
"""Create a service account and return its token.
|
|
497
|
+
|
|
498
|
+
Only an admin key from the service's ``SERVICE_TOKENS`` may call
|
|
499
|
+
this. A name that already exists raises
|
|
500
|
+
:class:`SandboxConflictError`; an invalid name raises
|
|
501
|
+
:class:`SandboxValidationError`.
|
|
502
|
+
"""
|
|
503
|
+
|
|
504
|
+
data = await self._request("POST", "/service-accounts", json={"name": name})
|
|
505
|
+
return ServiceAccount(**data)
|
|
506
|
+
|
|
507
|
+
async def remove_service_account(self, name: str) -> None:
|
|
508
|
+
"""Revoke a service account's token and remove the account.
|
|
509
|
+
|
|
510
|
+
An unknown name raises :class:`SandboxNotFoundError`; the ``admin``
|
|
511
|
+
account raises :class:`SandboxConflictError`.
|
|
512
|
+
"""
|
|
513
|
+
|
|
514
|
+
await self._request("DELETE", f"/service-accounts/{name}")
|
|
515
|
+
|
|
469
516
|
async def create_http_proxy(
|
|
470
517
|
self,
|
|
471
518
|
*,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
drukbox_sdk/__init__.py,sha256=7LHAef5BXUaEN_pk9pXeowyV8CuQxWnbghANSZZTcPM,970
|
|
2
|
-
drukbox_sdk/api.py,sha256=tcVaHQom-O2Xq8BbThFTjiRt7zHkWw3a6WYwC4rW3c0,22300
|
|
3
|
-
drukbox_sdk/exceptions.py,sha256=Vzs4zVOywWsWfiZm6vgQ65ScI3Nn7zLGVj-8eT1YAR0,2224
|
|
4
|
-
drukbox_python_sdk-0.2.0.dist-info/METADATA,sha256=3XKJgIsi_BHqaCuNhNaOSa6MSje_67u40BlCATpm3s4,5870
|
|
5
|
-
drukbox_python_sdk-0.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
6
|
-
drukbox_python_sdk-0.2.0.dist-info/licenses/LICENSE,sha256=zM66rWL3BWGJBa_mZPnM0WUomSSZ1xtWVtGQovQ71M8,1078
|
|
7
|
-
drukbox_python_sdk-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|