mrok 0.1.6__py3-none-any.whl → 0.1.8__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.
Files changed (66) hide show
  1. mrok/__init__.py +6 -0
  2. mrok/agent/__init__.py +0 -0
  3. mrok/agent/sidecar/__init__.py +3 -0
  4. mrok/agent/sidecar/app.py +30 -0
  5. mrok/agent/sidecar/main.py +27 -0
  6. mrok/agent/ziticorn.py +29 -0
  7. mrok/cli/__init__.py +3 -0
  8. mrok/cli/commands/__init__.py +7 -0
  9. mrok/cli/commands/admin/__init__.py +12 -0
  10. mrok/cli/commands/admin/bootstrap.py +58 -0
  11. mrok/cli/commands/admin/list/__init__.py +8 -0
  12. mrok/cli/commands/admin/list/extensions.py +144 -0
  13. mrok/cli/commands/admin/list/instances.py +167 -0
  14. mrok/cli/commands/admin/register/__init__.py +8 -0
  15. mrok/cli/commands/admin/register/extensions.py +46 -0
  16. mrok/cli/commands/admin/register/instances.py +60 -0
  17. mrok/cli/commands/admin/unregister/__init__.py +8 -0
  18. mrok/cli/commands/admin/unregister/extensions.py +33 -0
  19. mrok/cli/commands/admin/unregister/instances.py +34 -0
  20. mrok/cli/commands/admin/utils.py +49 -0
  21. mrok/cli/commands/agent/__init__.py +6 -0
  22. mrok/cli/commands/agent/run/__init__.py +7 -0
  23. mrok/cli/commands/agent/run/asgi.py +49 -0
  24. mrok/cli/commands/agent/run/sidecar.py +54 -0
  25. mrok/cli/commands/controller/__init__.py +7 -0
  26. mrok/cli/commands/controller/openapi.py +47 -0
  27. mrok/cli/commands/controller/run.py +87 -0
  28. mrok/cli/main.py +97 -0
  29. mrok/cli/rich.py +18 -0
  30. mrok/conf.py +32 -0
  31. mrok/controller/__init__.py +0 -0
  32. mrok/controller/app.py +62 -0
  33. mrok/controller/auth.py +87 -0
  34. mrok/controller/dependencies/__init__.py +4 -0
  35. mrok/controller/dependencies/conf.py +7 -0
  36. mrok/controller/dependencies/ziti.py +27 -0
  37. mrok/controller/openapi/__init__.py +3 -0
  38. mrok/controller/openapi/examples.py +44 -0
  39. mrok/controller/openapi/utils.py +35 -0
  40. mrok/controller/pagination.py +79 -0
  41. mrok/controller/routes.py +294 -0
  42. mrok/controller/schemas.py +67 -0
  43. mrok/errors.py +2 -0
  44. mrok/http/__init__.py +0 -0
  45. mrok/http/config.py +65 -0
  46. mrok/http/forwarder.py +299 -0
  47. mrok/http/lifespan.py +10 -0
  48. mrok/http/master.py +90 -0
  49. mrok/http/protocol.py +11 -0
  50. mrok/http/server.py +14 -0
  51. mrok/logging.py +76 -0
  52. mrok/ziti/__init__.py +15 -0
  53. mrok/ziti/api.py +481 -0
  54. mrok/ziti/bootstrap.py +71 -0
  55. mrok/ziti/constants.py +9 -0
  56. mrok/ziti/errors.py +25 -0
  57. mrok/ziti/identities.py +169 -0
  58. mrok/ziti/pki.py +52 -0
  59. mrok/ziti/services.py +87 -0
  60. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/METADATA +7 -9
  61. mrok-0.1.8.dist-info/RECORD +64 -0
  62. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/WHEEL +1 -2
  63. mrok-0.1.6.dist-info/RECORD +0 -6
  64. mrok-0.1.6.dist-info/top_level.txt +0 -1
  65. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/entry_points.txt +0 -0
  66. {mrok-0.1.6.dist-info → mrok-0.1.8.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,3 @@
1
+ from mrok.controller.openapi.utils import generate_openapi_spec
2
+
3
+ __all__ = ["generate_openapi_spec"]
@@ -0,0 +1,44 @@
1
+ from mrok.ziti.constants import MROK_SERVICE_TAG_NAME, MROK_VERSION_TAG_NAME
2
+
3
+ EXTENSION_RESPONSE = {
4
+ "id": "5Jm3PpLQ4mdzqXNRszhE0G",
5
+ "name": "ext-1234-5678",
6
+ "extension": {"id": "EXT-1234-5678"},
7
+ "tags": {"account": "ACC-5555-3333", MROK_VERSION_TAG_NAME: "1.0"},
8
+ }
9
+
10
+
11
+ INSTANCE_RESPONSE = {
12
+ "id": "h.KUkPOyZ4",
13
+ "name": "ins-1234-5678-0001.ext-1234-5678",
14
+ "extension": {"id": "EXT-1234-5678"},
15
+ "instance": {"id": "INS-1234-5678-0001"},
16
+ "tags": {
17
+ "account": "ACC-5555-3333",
18
+ MROK_VERSION_TAG_NAME: "1.0",
19
+ MROK_SERVICE_TAG_NAME: "ext-1234-5678",
20
+ },
21
+ }
22
+
23
+ INSTANCE_CREATE_RESPONSE = {
24
+ "id": "h.KUkPOyZ4",
25
+ "name": "ins-1234-5678-0001.ext-1234-5678",
26
+ "extension": {"id": "EXT-1234-5678"},
27
+ "instance": {"id": "INS-1234-5678-0001"},
28
+ "identity": {
29
+ "ztAPI": "https://ziti.exts.platform.softwareone.com/edge/client/v1",
30
+ "ztAPIs": None,
31
+ "configTypes": None,
32
+ "id": {
33
+ "key": "pem:-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
34
+ "cert": "pem:-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n",
35
+ "ca": "pem:-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n",
36
+ },
37
+ "enableHa": None,
38
+ },
39
+ "tags": {
40
+ "account": "ACC-5555-3333",
41
+ MROK_VERSION_TAG_NAME: "1.0",
42
+ MROK_SERVICE_TAG_NAME: "ext-1234-5678",
43
+ },
44
+ }
@@ -0,0 +1,35 @@
1
+ from fastapi import FastAPI
2
+ from fastapi.openapi.utils import get_openapi
3
+
4
+ from mrok.conf import Settings
5
+
6
+
7
+ def generate_openapi_spec(app: FastAPI, settings: Settings):
8
+ if app.openapi_schema: # pragma: no cover
9
+ return app.openapi_schema
10
+
11
+ # for api_route in app.routes:
12
+ # if isinstance(api_route, APIRoute):
13
+ # for dep in api_route.dependant.dependencies:
14
+ # if dep.call and isinstance(dep.call, RQLQuery):
15
+ # api_route.description = (
16
+ # f"{api_route.description}\n\n"
17
+ # "## Available RQL filters\n\n"
18
+ # f"{dep.call.rules.get_documentation()}"
19
+ # )
20
+
21
+ spec = get_openapi(
22
+ title=app.title,
23
+ version=app.version,
24
+ openapi_version=app.openapi_version,
25
+ description=app.description,
26
+ tags=app.openapi_tags,
27
+ routes=app.routes,
28
+ )
29
+ # spec = inject_code_samples(
30
+ # spec,
31
+ # SnippetRenderer(),
32
+ # settings.api_base_url,
33
+ # )
34
+ app.openapi_schema = spec
35
+ return app.openapi_schema
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ from typing import Any
5
+
6
+ from fastapi import Query
7
+ from fastapi_pagination import create_page, resolve_params
8
+ from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams
9
+ from pydantic import BaseModel, Field
10
+
11
+ from mrok.controller.schemas import BaseSchema
12
+ from mrok.ziti.api import BaseZitiAPI
13
+
14
+
15
+ class MetaPagination(BaseModel):
16
+ limit: int
17
+ offset: int
18
+ total: int
19
+
20
+
21
+ class Meta(BaseModel):
22
+ pagination: MetaPagination
23
+
24
+
25
+ class LimitOffsetParams(BaseModel, AbstractParams):
26
+ limit: int = Query(50, ge=0, le=1000, description="Page size limit")
27
+ offset: int = Query(0, ge=0, description="Page offset")
28
+
29
+ def to_raw_params(self) -> RawParams:
30
+ return RawParams( # pragma: no cover
31
+ limit=self.limit,
32
+ offset=self.offset,
33
+ )
34
+
35
+
36
+ class LimitOffsetPage[S: BaseSchema](AbstractPage[S]):
37
+ data: list[S]
38
+ meta: Meta = Field(alias="$meta")
39
+
40
+ __params_type__ = LimitOffsetParams # type: ignore
41
+
42
+ @classmethod
43
+ def create(
44
+ cls,
45
+ items: Sequence[S],
46
+ params: AbstractParams,
47
+ *,
48
+ total: int | None = None,
49
+ **kwargs: Any,
50
+ ) -> LimitOffsetPage[S]:
51
+ if not isinstance(params, LimitOffsetParams): # pragma: no cover
52
+ raise TypeError("params must be of type LimitOffsetParams")
53
+ return cls( # type: ignore
54
+ data=items,
55
+ meta=Meta(
56
+ pagination=MetaPagination(
57
+ limit=params.limit,
58
+ offset=params.offset,
59
+ total=total,
60
+ )
61
+ ),
62
+ )
63
+
64
+
65
+ async def paginate[S: BaseSchema](
66
+ api: BaseZitiAPI,
67
+ endpoint: str,
68
+ schema_cls: type[S],
69
+ extra_params: dict | None = None,
70
+ ) -> AbstractPage[S]:
71
+ params: LimitOffsetParams = resolve_params()
72
+ page = await api.get_page(endpoint, params.limit, params.offset, extra_params)
73
+ pagination_meta = page["meta"]["pagination"]
74
+ total = pagination_meta["totalCount"]
75
+ return create_page(
76
+ [schema_cls(**item) for item in page["data"]],
77
+ params=params,
78
+ total=total,
79
+ )
@@ -0,0 +1,294 @@
1
+ import logging
2
+ from typing import Annotated
3
+
4
+ from fastapi import APIRouter, Body, HTTPException, status
5
+
6
+ from mrok.controller.dependencies import AppSettings, ZitiClientAPI, ZitiManagementAPI
7
+ from mrok.controller.openapi import examples
8
+ from mrok.controller.pagination import LimitOffsetPage, paginate
9
+ from mrok.controller.schemas import ExtensionCreate, ExtensionRead, InstanceCreate, InstanceRead
10
+ from mrok.ziti.constants import MROK_SERVICE_TAG_NAME
11
+ from mrok.ziti.errors import (
12
+ ConfigTypeNotFoundError,
13
+ ProxyIdentityNotFoundError,
14
+ ServiceAlreadyRegisteredError,
15
+ ServiceNotFoundError,
16
+ )
17
+ from mrok.ziti.identities import register_instance, unregister_instance
18
+ from mrok.ziti.services import register_extension, unregister_extension
19
+
20
+ logger = logging.getLogger("mrok.controller")
21
+
22
+ router = APIRouter()
23
+
24
+
25
+ async def fetch_extension_or_404(mgmt_api: ZitiManagementAPI, id_or_extension_id: str):
26
+ service = await mgmt_api.search_service(id_or_extension_id)
27
+ if not service:
28
+ raise HTTPException(
29
+ status_code=status.HTTP_404_NOT_FOUND,
30
+ )
31
+ return service
32
+
33
+
34
+ async def fetch_instance_or_404(
35
+ mgmt_api: ZitiManagementAPI, id_or_extension_id: str, id_or_instance_id: str
36
+ ):
37
+ service = await fetch_extension_or_404(mgmt_api, id_or_extension_id)
38
+ if id_or_instance_id.startswith("INS-"):
39
+ id_or_name = f"{id_or_instance_id}.{service['name']}"
40
+ else:
41
+ id_or_name = id_or_instance_id
42
+ identity = await mgmt_api.search_identity(id_or_name)
43
+ if not identity:
44
+ raise HTTPException(
45
+ status_code=status.HTTP_404_NOT_FOUND,
46
+ )
47
+ return identity
48
+
49
+
50
+ @router.post(
51
+ "",
52
+ response_model=ExtensionRead,
53
+ responses={
54
+ 201: {
55
+ "description": "Extension",
56
+ "content": {
57
+ "application/json": {
58
+ "example": examples.EXTENSION_RESPONSE,
59
+ }
60
+ },
61
+ },
62
+ },
63
+ status_code=status.HTTP_201_CREATED,
64
+ tags=["Extensions"],
65
+ )
66
+ async def create_extension(
67
+ settings: AppSettings,
68
+ mgmt_api: ZitiManagementAPI,
69
+ data: Annotated[
70
+ ExtensionCreate,
71
+ Body(
72
+ openapi_examples={
73
+ "create_extension": {
74
+ "summary": "Create an Extension",
75
+ "description": ("Create a new Extension."),
76
+ "value": {
77
+ "extension": {"id": "EXT-1234-5678"},
78
+ "tags": {"account": "ACC-5555-3333"},
79
+ },
80
+ }
81
+ }
82
+ ),
83
+ ],
84
+ ):
85
+ try:
86
+ service = await register_extension(settings, mgmt_api, data.extension.id, data.tags)
87
+ return ExtensionRead(
88
+ id=service["id"],
89
+ name=service["name"],
90
+ tags=service["tags"],
91
+ )
92
+ except (ProxyIdentityNotFoundError, ConfigTypeNotFoundError) as e:
93
+ raise HTTPException(
94
+ status_code=status.HTTP_400_BAD_REQUEST,
95
+ detail=f"OpenZiti not configured properly: {e}",
96
+ ) from e
97
+ except ServiceAlreadyRegisteredError as e:
98
+ raise HTTPException(
99
+ status_code=status.HTTP_400_BAD_REQUEST,
100
+ detail=str(e),
101
+ ) from e
102
+
103
+
104
+ @router.get(
105
+ "/{id_or_extension_id}",
106
+ response_model=ExtensionRead,
107
+ responses={
108
+ 200: {
109
+ "description": "Extension",
110
+ "content": {"application/json": {"example": examples.EXTENSION_RESPONSE}},
111
+ },
112
+ },
113
+ dependencies=[],
114
+ tags=["Extensions"],
115
+ )
116
+ async def get_extension_by_id_or_extension_id(
117
+ mgmt_api: ZitiManagementAPI,
118
+ id_or_extension_id: str,
119
+ ):
120
+ return ExtensionRead(**(await fetch_extension_or_404(mgmt_api, id_or_extension_id)))
121
+
122
+
123
+ @router.delete(
124
+ "/{id_or_extension_id}",
125
+ status_code=status.HTTP_204_NO_CONTENT,
126
+ tags=["Extensions"],
127
+ )
128
+ async def delete_instance_by_id_or_extension_id(
129
+ settings: AppSettings,
130
+ mgmt_api: ZitiManagementAPI,
131
+ id_or_extension_id: str,
132
+ ):
133
+ try:
134
+ await unregister_extension(settings, mgmt_api, id_or_extension_id)
135
+ except ServiceNotFoundError:
136
+ raise HTTPException(
137
+ status_code=status.HTTP_404_NOT_FOUND,
138
+ )
139
+
140
+
141
+ @router.get(
142
+ "",
143
+ response_model=LimitOffsetPage[ExtensionRead],
144
+ responses={
145
+ 200: {
146
+ "description": "List of Extensions",
147
+ "content": {
148
+ "application/json": {
149
+ "example": {
150
+ "data": [examples.EXTENSION_RESPONSE],
151
+ "$meta": {
152
+ "pagination": {
153
+ "total": 1,
154
+ "limit": 10,
155
+ "offset": 0,
156
+ },
157
+ },
158
+ },
159
+ },
160
+ },
161
+ },
162
+ },
163
+ tags=["Extensions"],
164
+ )
165
+ async def get_extensions(
166
+ mgmt_api: ZitiManagementAPI,
167
+ ):
168
+ return await paginate(mgmt_api, "/services", ExtensionRead)
169
+
170
+
171
+ @router.post(
172
+ "/{id_or_extension_id}/instances",
173
+ response_model=InstanceRead,
174
+ responses={
175
+ 201: {
176
+ "description": "Instance",
177
+ "content": {
178
+ "application/json": {
179
+ "example": examples.INSTANCE_CREATE_RESPONSE,
180
+ }
181
+ },
182
+ },
183
+ },
184
+ status_code=status.HTTP_201_CREATED,
185
+ tags=["Instances"],
186
+ )
187
+ async def create_extension_instances(
188
+ mgmt_api: ZitiManagementAPI,
189
+ client_api: ZitiClientAPI,
190
+ id_or_extension_id: str,
191
+ data: Annotated[
192
+ InstanceCreate,
193
+ Body(
194
+ openapi_examples={
195
+ "create_extension": {
196
+ "summary": "Create an Extension Instance",
197
+ "description": ("Create a new Instance of an Extension."),
198
+ "value": {
199
+ "instance": {"id": "INS-1234-5678-0001"},
200
+ "tags": {"account": "ACC-5555-3333"},
201
+ },
202
+ }
203
+ }
204
+ ),
205
+ ],
206
+ ):
207
+ service = await fetch_extension_or_404(mgmt_api, id_or_extension_id)
208
+ identity, identity_file = await register_instance(
209
+ mgmt_api, client_api, service["name"], data.instance.id, data.tags
210
+ )
211
+ return InstanceRead(
212
+ id=identity["id"],
213
+ name=identity["name"],
214
+ identity=identity_file,
215
+ tags=identity["tags"],
216
+ )
217
+
218
+
219
+ @router.get(
220
+ "/{id_or_extension_id}/instances",
221
+ response_model=LimitOffsetPage[InstanceRead],
222
+ responses={
223
+ 200: {
224
+ "description": "List of Instances.",
225
+ "content": {
226
+ "application/json": {
227
+ "example": {
228
+ "data": [examples.INSTANCE_RESPONSE],
229
+ "$meta": {
230
+ "pagination": {
231
+ "total": 1,
232
+ "limit": 10,
233
+ "offset": 0,
234
+ },
235
+ },
236
+ },
237
+ },
238
+ },
239
+ },
240
+ },
241
+ dependencies=[],
242
+ tags=["Instances"],
243
+ )
244
+ async def list_extension_instances(
245
+ mgmt_api: ZitiManagementAPI,
246
+ id_or_extension_id: str,
247
+ ):
248
+ service = await fetch_extension_or_404(mgmt_api, id_or_extension_id)
249
+ return await paginate(
250
+ mgmt_api,
251
+ "/identities",
252
+ InstanceRead,
253
+ {"filter": f'tags.{MROK_SERVICE_TAG_NAME} = "{service["name"]}"'},
254
+ )
255
+
256
+
257
+ @router.get(
258
+ "/{id_or_extension_id}/instances/{id_or_instance_id}",
259
+ response_model=InstanceRead,
260
+ responses={
261
+ 200: {
262
+ "description": "Instance",
263
+ "content": {"application/json": {"example": examples.INSTANCE_RESPONSE}},
264
+ },
265
+ },
266
+ dependencies=[],
267
+ tags=["Instances"],
268
+ )
269
+ async def get_instance_by_id_or_instance_id(
270
+ mgmt_api: ZitiManagementAPI,
271
+ id_or_extension_id: str,
272
+ id_or_instance_id: str,
273
+ ):
274
+ identity = await fetch_instance_or_404(mgmt_api, id_or_extension_id, id_or_instance_id)
275
+ return InstanceRead(
276
+ id=identity["id"],
277
+ name=identity["name"],
278
+ tags=identity["tags"],
279
+ )
280
+
281
+
282
+ @router.delete(
283
+ "/{id_or_extension_id}/instances/{id_or_instance_id}",
284
+ status_code=status.HTTP_204_NO_CONTENT,
285
+ tags=["Instances"],
286
+ )
287
+ async def delete_instance_by_id_or_instance_id(
288
+ mgmt_api: ZitiManagementAPI,
289
+ id_or_extension_id: str,
290
+ id_or_instance_id: str,
291
+ ):
292
+ identity = await fetch_instance_or_404(mgmt_api, id_or_extension_id, id_or_instance_id)
293
+ instance_id, extension_id = identity["name"].split(".")
294
+ await unregister_instance(mgmt_api, extension_id, instance_id)
@@ -0,0 +1,67 @@
1
+ from typing import Annotated, Any
2
+
3
+ from pydantic import (
4
+ BaseModel,
5
+ ConfigDict,
6
+ Field,
7
+ computed_field,
8
+ )
9
+
10
+ from mrok.ziti.api import TagsType
11
+
12
+
13
+ class BaseSchema(BaseModel):
14
+ model_config = ConfigDict(from_attributes=True, extra="ignore")
15
+ tags: TagsType | None = None
16
+
17
+
18
+ class IdSchema(BaseModel):
19
+ id: str
20
+
21
+
22
+ class ExtensionIdSchema(BaseModel):
23
+ id: Annotated[str, Field(pattern=r"EXT-\d{4}-\d{4}")]
24
+
25
+
26
+ # For instance
27
+ class InstanceIdSchema(BaseModel):
28
+ id: Annotated[str, Field(pattern=r"INS-\d{4}-\d{4}-\d{4}")]
29
+
30
+
31
+ class ExtensionBase(BaseSchema):
32
+ extension: ExtensionIdSchema
33
+
34
+
35
+ class ExtensionRead(BaseSchema, IdSchema):
36
+ name: str
37
+
38
+ @computed_field
39
+ def extension(self) -> dict:
40
+ return {"id": self.name.upper()}
41
+
42
+
43
+ class ExtensionCreate(ExtensionBase):
44
+ pass
45
+
46
+
47
+ class InstanceBase(BaseSchema):
48
+ instance: InstanceIdSchema
49
+
50
+
51
+ class InstanceRead(BaseSchema, IdSchema):
52
+ name: str
53
+ identity: dict[str, Any] | None = None
54
+
55
+ @computed_field
56
+ def instance(self) -> dict:
57
+ instance_id, _ = self.name.split(".", 1)
58
+ return {"id": instance_id.upper()}
59
+
60
+ @computed_field
61
+ def extension(self) -> dict:
62
+ _, extension_id = self.name.split(".", 1)
63
+ return {"id": extension_id.upper()}
64
+
65
+
66
+ class InstanceCreate(InstanceBase):
67
+ pass
mrok/errors.py ADDED
@@ -0,0 +1,2 @@
1
+ class MrokError(Exception):
2
+ pass
mrok/http/__init__.py ADDED
File without changes
mrok/http/config.py ADDED
@@ -0,0 +1,65 @@
1
+ import json
2
+ import logging
3
+ import socket
4
+ from collections.abc import Callable
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import openziti
9
+ from uvicorn import config
10
+
11
+ from mrok.conf import get_settings
12
+ from mrok.http.protocol import MrokHttpToolsProtocol
13
+ from mrok.logging import setup_logging
14
+
15
+ logger = logging.getLogger("mrok.proxy")
16
+
17
+ config.LIFESPAN["auto"] = "mrok.http.lifespan:MrokLifespan"
18
+
19
+ ASGIApplication = config.ASGIApplication
20
+
21
+
22
+ class MrokBackendConfig(config.Config):
23
+ def __init__(
24
+ self,
25
+ app: ASGIApplication | Callable[..., Any] | str,
26
+ identity_file: str | Path,
27
+ ziti_load_timeout_ms: int = 5000,
28
+ backlog: int = 2048,
29
+ ):
30
+ self.identity_file = identity_file
31
+ self.ziti_load_timeout_ms = ziti_load_timeout_ms
32
+ self.service_name, self.identity_name, self.instance_id = self.get_identity_info(
33
+ identity_file
34
+ )
35
+ super().__init__(
36
+ app,
37
+ loop="asyncio",
38
+ http=MrokHttpToolsProtocol,
39
+ backlog=backlog,
40
+ )
41
+
42
+ def get_identity_info(self, identity_file: str | Path):
43
+ with open(identity_file) as f:
44
+ identity_data = json.load(f)
45
+ try:
46
+ identity_name = identity_data["mrok"]["identity"]
47
+ instance_id, service_name = identity_name.split(".", 1)
48
+ return service_name, identity_name, instance_id
49
+ except KeyError:
50
+ raise ValueError("Invalid identity file: identity file is not mrok compatible.")
51
+
52
+ def bind_socket(self) -> socket.socket:
53
+ logger.info(f"Connect to Ziti service '{self.service_name} ({self.instance_id})'")
54
+
55
+ ctx, err = openziti.load(str(self.identity_file), timeout=self.ziti_load_timeout_ms)
56
+ if err != 0:
57
+ raise RuntimeError(f"Failed to load Ziti identity from {self.identity_file}: {err}")
58
+
59
+ sock = ctx.bind(self.service_name)
60
+ sock.listen(self.backlog)
61
+ logger.info(f"listening on ziti service {self.service_name} for connections")
62
+ return sock
63
+
64
+ def configure_logging(self) -> None:
65
+ setup_logging(get_settings())