mrok 0.2.3__py3-none-any.whl → 0.4.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.
- mrok/agent/devtools/__init__.py +0 -0
- mrok/agent/devtools/__main__.py +34 -0
- mrok/agent/devtools/inspector/__init__.py +0 -0
- mrok/agent/devtools/inspector/__main__.py +25 -0
- mrok/agent/devtools/inspector/app.py +556 -0
- mrok/agent/devtools/inspector/server.py +18 -0
- mrok/agent/sidecar/app.py +9 -10
- mrok/agent/sidecar/main.py +35 -16
- mrok/agent/ziticorn.py +27 -18
- mrok/cli/commands/__init__.py +2 -1
- mrok/cli/commands/admin/list/instances.py +24 -4
- mrok/cli/commands/admin/register/extensions.py +2 -2
- mrok/cli/commands/admin/register/instances.py +3 -3
- mrok/cli/commands/admin/unregister/extensions.py +2 -2
- mrok/cli/commands/admin/unregister/instances.py +2 -2
- mrok/cli/commands/agent/__init__.py +2 -0
- mrok/cli/commands/agent/dev/__init__.py +7 -0
- mrok/cli/commands/agent/dev/console.py +25 -0
- mrok/cli/commands/agent/dev/web.py +37 -0
- mrok/cli/commands/agent/run/asgi.py +35 -16
- mrok/cli/commands/agent/run/sidecar.py +29 -13
- mrok/cli/commands/agent/utils.py +5 -0
- mrok/cli/commands/controller/run.py +1 -5
- mrok/cli/commands/proxy/__init__.py +6 -0
- mrok/cli/commands/proxy/run.py +49 -0
- mrok/cli/utils.py +5 -0
- mrok/conf.py +6 -0
- mrok/controller/auth.py +2 -2
- mrok/controller/routes/extensions.py +9 -7
- mrok/datastructures.py +159 -0
- mrok/http/config.py +3 -6
- mrok/http/constants.py +22 -0
- mrok/http/forwarder.py +62 -23
- mrok/http/lifespan.py +29 -0
- mrok/http/middlewares.py +143 -0
- mrok/http/types.py +43 -0
- mrok/http/utils.py +90 -0
- mrok/logging.py +22 -0
- mrok/master.py +269 -0
- mrok/metrics.py +139 -0
- mrok/proxy/__init__.py +3 -0
- mrok/proxy/app.py +73 -0
- mrok/proxy/dataclasses.py +12 -0
- mrok/proxy/main.py +58 -0
- mrok/proxy/streams.py +124 -0
- mrok/proxy/types.py +12 -0
- mrok/proxy/ziti.py +173 -0
- mrok/ziti/identities.py +50 -20
- mrok/ziti/services.py +8 -8
- {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/METADATA +7 -1
- mrok-0.4.0.dist-info/RECORD +92 -0
- {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/WHEEL +1 -1
- mrok/http/master.py +0 -132
- mrok-0.2.3.dist-info/RECORD +0 -66
- {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/entry_points.txt +0 -0
- {mrok-0.2.3.dist-info → mrok-0.4.0.dist-info}/licenses/LICENSE.txt +0 -0
mrok/proxy/streams.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from mrok.proxy.types import ConnectionCache, ConnectionKey
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CachedStreamReader:
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
reader: asyncio.StreamReader,
|
|
10
|
+
key: ConnectionKey,
|
|
11
|
+
manager: ConnectionCache,
|
|
12
|
+
):
|
|
13
|
+
self._reader = reader
|
|
14
|
+
self._key = key
|
|
15
|
+
self._manager = manager
|
|
16
|
+
|
|
17
|
+
async def read(self, n: int = -1) -> bytes:
|
|
18
|
+
try:
|
|
19
|
+
return await self._reader.read(n)
|
|
20
|
+
except (
|
|
21
|
+
asyncio.CancelledError,
|
|
22
|
+
asyncio.IncompleteReadError,
|
|
23
|
+
asyncio.LimitOverrunError,
|
|
24
|
+
BrokenPipeError,
|
|
25
|
+
ConnectionAbortedError,
|
|
26
|
+
ConnectionResetError,
|
|
27
|
+
RuntimeError,
|
|
28
|
+
TimeoutError,
|
|
29
|
+
UnicodeDecodeError,
|
|
30
|
+
):
|
|
31
|
+
asyncio.create_task(self._manager.invalidate(self._key))
|
|
32
|
+
raise
|
|
33
|
+
|
|
34
|
+
async def readexactly(self, n: int) -> bytes:
|
|
35
|
+
try:
|
|
36
|
+
return await self._reader.readexactly(n)
|
|
37
|
+
except (
|
|
38
|
+
asyncio.CancelledError,
|
|
39
|
+
asyncio.IncompleteReadError,
|
|
40
|
+
asyncio.LimitOverrunError,
|
|
41
|
+
BrokenPipeError,
|
|
42
|
+
ConnectionAbortedError,
|
|
43
|
+
ConnectionResetError,
|
|
44
|
+
RuntimeError,
|
|
45
|
+
TimeoutError,
|
|
46
|
+
UnicodeDecodeError,
|
|
47
|
+
):
|
|
48
|
+
asyncio.create_task(self._manager.invalidate(self._key))
|
|
49
|
+
raise
|
|
50
|
+
|
|
51
|
+
async def readline(self) -> bytes:
|
|
52
|
+
try:
|
|
53
|
+
return await self._reader.readline()
|
|
54
|
+
except (
|
|
55
|
+
asyncio.CancelledError,
|
|
56
|
+
asyncio.IncompleteReadError,
|
|
57
|
+
asyncio.LimitOverrunError,
|
|
58
|
+
BrokenPipeError,
|
|
59
|
+
ConnectionAbortedError,
|
|
60
|
+
ConnectionResetError,
|
|
61
|
+
RuntimeError,
|
|
62
|
+
TimeoutError,
|
|
63
|
+
UnicodeDecodeError,
|
|
64
|
+
):
|
|
65
|
+
asyncio.create_task(self._manager.invalidate(self._key))
|
|
66
|
+
raise
|
|
67
|
+
|
|
68
|
+
def at_eof(self) -> bool:
|
|
69
|
+
return self._reader.at_eof()
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def underlying(self) -> asyncio.StreamReader:
|
|
73
|
+
return self._reader
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CachedStreamWriter:
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
writer: asyncio.StreamWriter,
|
|
80
|
+
key: ConnectionKey,
|
|
81
|
+
manager: ConnectionCache,
|
|
82
|
+
):
|
|
83
|
+
self._writer = writer
|
|
84
|
+
self._key = key
|
|
85
|
+
self._manager = manager
|
|
86
|
+
|
|
87
|
+
def write(self, data: bytes) -> None:
|
|
88
|
+
try:
|
|
89
|
+
return self._writer.write(data)
|
|
90
|
+
except (RuntimeError, TypeError):
|
|
91
|
+
asyncio.create_task(self._manager.invalidate(self._key))
|
|
92
|
+
raise
|
|
93
|
+
|
|
94
|
+
async def drain(self) -> None:
|
|
95
|
+
try:
|
|
96
|
+
return await self._writer.drain()
|
|
97
|
+
except (
|
|
98
|
+
asyncio.CancelledError,
|
|
99
|
+
BrokenPipeError,
|
|
100
|
+
ConnectionAbortedError,
|
|
101
|
+
ConnectionResetError,
|
|
102
|
+
RuntimeError,
|
|
103
|
+
TimeoutError,
|
|
104
|
+
):
|
|
105
|
+
asyncio.create_task(self._manager.invalidate(self._key))
|
|
106
|
+
raise
|
|
107
|
+
|
|
108
|
+
def close(self) -> None:
|
|
109
|
+
return self._writer.close()
|
|
110
|
+
|
|
111
|
+
async def wait_closed(self) -> None:
|
|
112
|
+
try:
|
|
113
|
+
return await self._writer.wait_closed()
|
|
114
|
+
except (ConnectionResetError, BrokenPipeError):
|
|
115
|
+
asyncio.create_task(self._manager.invalidate(self._key))
|
|
116
|
+
raise
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def transport(self):
|
|
120
|
+
return self._writer.transport
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def underlying(self) -> asyncio.StreamWriter:
|
|
124
|
+
return self._writer
|
mrok/proxy/types.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from mrok.http.types import StreamReader, StreamWriter
|
|
6
|
+
|
|
7
|
+
ConnectionKey = tuple[str, str | None]
|
|
8
|
+
CachedStream = tuple[StreamReader, StreamWriter]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConnectionCache(Protocol):
|
|
12
|
+
async def invalidate(self, key: ConnectionKey) -> None: ...
|
mrok/proxy/ziti.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Ziti-backed connection manager for the proxy.
|
|
2
|
+
|
|
3
|
+
This manager owns creation of connections via an OpenZiti context, wraps
|
|
4
|
+
streams to observe IO errors, evicts idle entries, and serializes creation
|
|
5
|
+
per-key.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
# typing imports intentionally minimized
|
|
14
|
+
import openziti
|
|
15
|
+
|
|
16
|
+
from mrok.http.types import StreamReader, StreamWriter
|
|
17
|
+
from mrok.proxy.dataclasses import CachedStreamEntry
|
|
18
|
+
from mrok.proxy.streams import CachedStreamReader, CachedStreamWriter
|
|
19
|
+
from mrok.proxy.types import CachedStream, ConnectionKey
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger("mrok.proxy")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ZitiConnectionManager:
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
identity_file: str | Path,
|
|
28
|
+
ziti_timeout_ms: int = 10000,
|
|
29
|
+
ttl_seconds: float = 60.0,
|
|
30
|
+
purge_interval: float = 10.0,
|
|
31
|
+
):
|
|
32
|
+
self._identity_file = identity_file
|
|
33
|
+
self._ziti_ctx = None
|
|
34
|
+
self._ziti_timeout_ms = ziti_timeout_ms
|
|
35
|
+
self._ttl = float(ttl_seconds)
|
|
36
|
+
self._purge_interval = float(purge_interval)
|
|
37
|
+
self._cache: dict[ConnectionKey, CachedStreamEntry] = {}
|
|
38
|
+
self._lock = asyncio.Lock()
|
|
39
|
+
self._in_progress: dict[ConnectionKey, asyncio.Lock] = {}
|
|
40
|
+
self._purge_task: asyncio.Task | None = None
|
|
41
|
+
|
|
42
|
+
async def get(self, target: str) -> tuple[StreamReader, StreamWriter] | tuple[None, None]:
|
|
43
|
+
head, _, tail = target.partition(".")
|
|
44
|
+
terminator = target if head and tail else ""
|
|
45
|
+
service = tail if tail else head
|
|
46
|
+
r, w = await self._get_or_create_key((service, terminator))
|
|
47
|
+
return r, w
|
|
48
|
+
|
|
49
|
+
async def invalidate(self, key: ConnectionKey) -> None:
|
|
50
|
+
async with self._lock:
|
|
51
|
+
item = self._cache.pop(key, None)
|
|
52
|
+
if item is None:
|
|
53
|
+
return
|
|
54
|
+
await self._close_writer(item.writer)
|
|
55
|
+
|
|
56
|
+
async def start(self) -> None:
|
|
57
|
+
if self._ziti_ctx is None:
|
|
58
|
+
ctx, err = openziti.load(str(self._identity_file), timeout=self._ziti_timeout_ms)
|
|
59
|
+
if err != 0:
|
|
60
|
+
raise Exception(f"Cannot create a Ziti context from the identity file: {err}")
|
|
61
|
+
self._ziti_ctx = ctx
|
|
62
|
+
if self._purge_task is None:
|
|
63
|
+
self._purge_task = asyncio.create_task(self._purge_loop())
|
|
64
|
+
logger.info("Ziti connection manager started")
|
|
65
|
+
|
|
66
|
+
async def stop(self) -> None:
|
|
67
|
+
if self._purge_task is not None:
|
|
68
|
+
self._purge_task.cancel()
|
|
69
|
+
try:
|
|
70
|
+
await self._purge_task
|
|
71
|
+
except asyncio.CancelledError:
|
|
72
|
+
logger.debug("Purge task was cancelled")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.warning(f"An error occurred stopping the purge task: {e}")
|
|
75
|
+
self._purge_task = None
|
|
76
|
+
logger.info("Ziti connection manager stopped")
|
|
77
|
+
|
|
78
|
+
async with self._lock:
|
|
79
|
+
items = list(self._cache.items())
|
|
80
|
+
self._cache.clear()
|
|
81
|
+
|
|
82
|
+
for _, item in items:
|
|
83
|
+
await self._close_writer(item.writer)
|
|
84
|
+
|
|
85
|
+
async def _purge_loop(self) -> None:
|
|
86
|
+
try:
|
|
87
|
+
while True:
|
|
88
|
+
await asyncio.sleep(self._purge_interval)
|
|
89
|
+
await self._purge_once()
|
|
90
|
+
except asyncio.CancelledError:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
async def _purge_once(self) -> None:
|
|
94
|
+
to_close: list[tuple[StreamReader, StreamWriter]] = []
|
|
95
|
+
async with self._lock:
|
|
96
|
+
now = time.time()
|
|
97
|
+
for key, item in list(self._cache.items()):
|
|
98
|
+
if now - item.last_access > self._ttl:
|
|
99
|
+
to_close.append((item.reader, item.writer))
|
|
100
|
+
del self._cache[key]
|
|
101
|
+
|
|
102
|
+
for _, writer in to_close:
|
|
103
|
+
writer.close()
|
|
104
|
+
await self._close_writer(writer)
|
|
105
|
+
|
|
106
|
+
def _is_writer_closed(self, writer: StreamWriter) -> bool:
|
|
107
|
+
return writer.transport.is_closing()
|
|
108
|
+
|
|
109
|
+
async def _close_writer(self, writer: StreamWriter) -> None:
|
|
110
|
+
writer.close()
|
|
111
|
+
try:
|
|
112
|
+
await writer.wait_closed()
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.debug(f"Error closing writer: {e}")
|
|
115
|
+
|
|
116
|
+
async def _get_or_create_key(self, key: ConnectionKey) -> CachedStream:
|
|
117
|
+
"""Internal: create or return a cached wrapped pair for the concrete key."""
|
|
118
|
+
await self._purge_once()
|
|
119
|
+
to_close = None
|
|
120
|
+
async with self._lock:
|
|
121
|
+
if key in self._cache:
|
|
122
|
+
now = time.time()
|
|
123
|
+
item = self._cache[key]
|
|
124
|
+
reader, writer = item.reader, item.writer
|
|
125
|
+
if not self._is_writer_closed(writer) and not reader.at_eof():
|
|
126
|
+
self._cache[key] = CachedStreamEntry(reader, writer, now)
|
|
127
|
+
return reader, writer
|
|
128
|
+
to_close = writer
|
|
129
|
+
del self._cache[key]
|
|
130
|
+
|
|
131
|
+
lock = self._in_progress.get(key)
|
|
132
|
+
if lock is None:
|
|
133
|
+
lock = asyncio.Lock()
|
|
134
|
+
self._in_progress[key] = lock
|
|
135
|
+
|
|
136
|
+
if to_close:
|
|
137
|
+
await self._close_writer(to_close)
|
|
138
|
+
|
|
139
|
+
async with lock:
|
|
140
|
+
try:
|
|
141
|
+
# # double-check cache after acquiring the per-key lock
|
|
142
|
+
# async with self._lock:
|
|
143
|
+
# now = time.time()
|
|
144
|
+
# if key in self._cache:
|
|
145
|
+
# r, w, _ = self._cache[key]
|
|
146
|
+
# if not self._is_writer_closed(w) and not r.at_eof():
|
|
147
|
+
# self._cache[key] = (r, w, now)
|
|
148
|
+
# return r, w
|
|
149
|
+
|
|
150
|
+
# perform creation via ziti context
|
|
151
|
+
extension, instance = key
|
|
152
|
+
logger.info(f"Create connection to {extension}: {instance}")
|
|
153
|
+
# loop = asyncio.get_running_loop()
|
|
154
|
+
# sock = await loop.run_in_executor(None, self._ziti_ctx.connect,
|
|
155
|
+
# extension, instance)
|
|
156
|
+
if instance:
|
|
157
|
+
sock = self._ziti_ctx.connect(
|
|
158
|
+
extension, terminator=instance
|
|
159
|
+
) # , terminator=instance)
|
|
160
|
+
else:
|
|
161
|
+
sock = self._ziti_ctx.connect(extension)
|
|
162
|
+
orig_reader, orig_writer = await asyncio.open_connection(sock=sock)
|
|
163
|
+
|
|
164
|
+
reader = CachedStreamReader(orig_reader, key, self)
|
|
165
|
+
writer = CachedStreamWriter(orig_writer, key, self)
|
|
166
|
+
|
|
167
|
+
async with self._lock:
|
|
168
|
+
self._cache[key] = CachedStreamEntry(reader, writer, time.time())
|
|
169
|
+
|
|
170
|
+
return reader, writer
|
|
171
|
+
finally:
|
|
172
|
+
async with self._lock:
|
|
173
|
+
self._in_progress.pop(key, None)
|
mrok/ziti/identities.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
import logging
|
|
2
3
|
from typing import Any
|
|
3
4
|
|
|
4
5
|
import jwt
|
|
5
6
|
|
|
7
|
+
from mrok.conf import Settings
|
|
6
8
|
from mrok.ziti import pki
|
|
7
9
|
from mrok.ziti.api import TagsType, ZitiClientAPI, ZitiManagementAPI
|
|
8
10
|
from mrok.ziti.constants import (
|
|
@@ -16,31 +18,37 @@ from mrok.ziti.errors import (
|
|
|
16
18
|
ServiceNotFoundError,
|
|
17
19
|
UserIdentityNotFoundError,
|
|
18
20
|
)
|
|
21
|
+
from mrok.ziti.services import register_service, unregister_service
|
|
19
22
|
|
|
20
23
|
logger = logging.getLogger("mrok.ziti")
|
|
21
24
|
|
|
22
25
|
|
|
23
|
-
async def
|
|
26
|
+
async def register_identity(
|
|
27
|
+
settings: Settings,
|
|
24
28
|
mgmt_api: ZitiManagementAPI,
|
|
25
29
|
client_api: ZitiClientAPI,
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
service_external_id: str,
|
|
31
|
+
identity_external_id: str,
|
|
28
32
|
tags: TagsType | None = None,
|
|
29
33
|
):
|
|
30
|
-
service_name =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
service_name = service_external_id.lower()
|
|
35
|
+
identity_tags = copy.copy(tags or {})
|
|
36
|
+
identity_tags[MROK_SERVICE_TAG_NAME] = service_name
|
|
37
|
+
identity_tags[MROK_IDENTITY_TYPE_TAG_NAME] = MROK_IDENTITY_TYPE_TAG_VALUE_INSTANCE
|
|
34
38
|
service = await mgmt_api.search_service(service_name)
|
|
35
39
|
if not service:
|
|
36
|
-
raise ServiceNotFoundError(f"A service with name `{
|
|
40
|
+
raise ServiceNotFoundError(f"A service with name `{service_external_id}` does not exists.")
|
|
37
41
|
|
|
38
|
-
identity_name = f"{
|
|
42
|
+
identity_name = f"{identity_external_id.lower()}.{service_name}"
|
|
39
43
|
service_policy_name = f"{identity_name}:bind"
|
|
44
|
+
self_service_policy_name = f"self.{service_policy_name}"
|
|
40
45
|
|
|
41
46
|
identity = await mgmt_api.search_identity(identity_name)
|
|
42
47
|
if identity:
|
|
43
48
|
service_policy = await mgmt_api.search_service_policy(service_policy_name)
|
|
49
|
+
if service_policy:
|
|
50
|
+
await mgmt_api.delete_service_policy(service_policy["id"])
|
|
51
|
+
service_policy = await mgmt_api.search_service_policy(self_service_policy_name)
|
|
44
52
|
if service_policy:
|
|
45
53
|
await mgmt_api.delete_service_policy(service_policy["id"])
|
|
46
54
|
router_policy = await mgmt_api.search_router_policy(identity_name)
|
|
@@ -48,7 +56,7 @@ async def register_instance(
|
|
|
48
56
|
await mgmt_api.delete_router_policy(router_policy["id"])
|
|
49
57
|
await mgmt_api.delete_identity(identity["id"])
|
|
50
58
|
|
|
51
|
-
identity_id = await mgmt_api.create_user_identity(identity_name, tags=
|
|
59
|
+
identity_id = await mgmt_api.create_user_identity(identity_name, tags=identity_tags)
|
|
52
60
|
identity = await mgmt_api.get_identity(identity_id)
|
|
53
61
|
|
|
54
62
|
identity_json = await _enroll_identity(
|
|
@@ -58,33 +66,55 @@ async def register_instance(
|
|
|
58
66
|
identity,
|
|
59
67
|
mrok={
|
|
60
68
|
"identity": identity_name,
|
|
61
|
-
"extension":
|
|
62
|
-
"instance":
|
|
69
|
+
"extension": service_external_id,
|
|
70
|
+
"instance": identity_external_id,
|
|
71
|
+
"domain": settings.proxy.domain,
|
|
72
|
+
"tags": identity_tags,
|
|
63
73
|
},
|
|
64
74
|
)
|
|
65
75
|
|
|
76
|
+
self_service = await mgmt_api.search_service(identity_name)
|
|
77
|
+
if not self_service:
|
|
78
|
+
self_service = await register_service(settings, mgmt_api, identity_name, tags)
|
|
79
|
+
|
|
66
80
|
await mgmt_api.create_bind_service_policy(service_policy_name, service["id"], identity_id)
|
|
81
|
+
await mgmt_api.create_bind_service_policy(
|
|
82
|
+
self_service_policy_name,
|
|
83
|
+
self_service["id"],
|
|
84
|
+
identity_id,
|
|
85
|
+
)
|
|
67
86
|
await mgmt_api.create_router_policy(identity_name, identity_id)
|
|
68
87
|
|
|
69
88
|
return identity, identity_json
|
|
70
89
|
|
|
71
90
|
|
|
72
|
-
async def
|
|
91
|
+
async def unregister_identity(
|
|
92
|
+
settings: Settings,
|
|
73
93
|
mgmt_api: ZitiManagementAPI,
|
|
74
|
-
|
|
75
|
-
|
|
94
|
+
service_external_id: str,
|
|
95
|
+
identity_external_id: str,
|
|
76
96
|
):
|
|
77
|
-
service_name =
|
|
97
|
+
service_name = service_external_id.lower()
|
|
78
98
|
service = await mgmt_api.search_service(service_name)
|
|
79
99
|
if not service:
|
|
80
|
-
raise ServiceNotFoundError(f"A service with name `{
|
|
100
|
+
raise ServiceNotFoundError(f"A service with name `{service_external_id}` does not exists.")
|
|
81
101
|
|
|
82
|
-
identity_name = f"{
|
|
102
|
+
identity_name = f"{identity_external_id.lower()}.{service_name}"
|
|
83
103
|
service_policy_name = f"{identity_name}:bind"
|
|
84
104
|
|
|
85
105
|
identity = await mgmt_api.search_identity(identity_name)
|
|
86
106
|
if not identity:
|
|
87
|
-
raise UserIdentityNotFoundError(f"
|
|
107
|
+
raise UserIdentityNotFoundError(f"Identity `{identity_external_id}` not found.")
|
|
108
|
+
|
|
109
|
+
self_service_policy_name = f"self.{service_policy_name}"
|
|
110
|
+
|
|
111
|
+
service_policy = await mgmt_api.search_service_policy(self_service_policy_name)
|
|
112
|
+
if service_policy:
|
|
113
|
+
await mgmt_api.delete_service_policy(service_policy["id"])
|
|
114
|
+
|
|
115
|
+
self_service = await mgmt_api.search_service(identity_name)
|
|
116
|
+
if self_service:
|
|
117
|
+
await unregister_service(settings, mgmt_api, identity_name)
|
|
88
118
|
|
|
89
119
|
service_policy = await mgmt_api.search_service_policy(service_policy_name)
|
|
90
120
|
if service_policy:
|
|
@@ -120,7 +150,7 @@ async def _enroll_identity(
|
|
|
120
150
|
client_api: ZitiClientAPI,
|
|
121
151
|
identity_id: str,
|
|
122
152
|
identity: dict[str, Any] | None = None,
|
|
123
|
-
mrok: dict[str, str] | None = None,
|
|
153
|
+
mrok: dict[str, str | dict] | None = None,
|
|
124
154
|
):
|
|
125
155
|
if identity is None:
|
|
126
156
|
identity = await mgmt_api.get_identity(identity_id)
|
mrok/ziti/services.py
CHANGED
|
@@ -13,10 +13,10 @@ from mrok.ziti.errors import (
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
async def
|
|
17
|
-
settings: Settings, mgmt_api: ZitiManagementAPI,
|
|
16
|
+
async def register_service(
|
|
17
|
+
settings: Settings, mgmt_api: ZitiManagementAPI, external_id: str, tags: TagsType | None
|
|
18
18
|
) -> dict[str, Any]:
|
|
19
|
-
service_name =
|
|
19
|
+
service_name = external_id.lower()
|
|
20
20
|
registered = False
|
|
21
21
|
proxy_identity = await mgmt_api.search_identity(settings.proxy.identity)
|
|
22
22
|
if not proxy_identity:
|
|
@@ -58,17 +58,17 @@ async def register_extension(
|
|
|
58
58
|
await mgmt_api.create_service_router_policy(service_name, service_id, tags=tags)
|
|
59
59
|
registered = True
|
|
60
60
|
if not registered:
|
|
61
|
-
raise ServiceAlreadyRegisteredError(f"
|
|
61
|
+
raise ServiceAlreadyRegisteredError(f"Service `{external_id}` already registered.")
|
|
62
62
|
return service
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
async def
|
|
66
|
-
settings: Settings, mgmt_api: ZitiManagementAPI,
|
|
65
|
+
async def unregister_service(
|
|
66
|
+
settings: Settings, mgmt_api: ZitiManagementAPI, external_id: str
|
|
67
67
|
) -> None:
|
|
68
|
-
service_name =
|
|
68
|
+
service_name = external_id.lower()
|
|
69
69
|
service = await mgmt_api.search_service(service_name)
|
|
70
70
|
if not service:
|
|
71
|
-
raise ServiceNotFoundError(f"
|
|
71
|
+
raise ServiceNotFoundError(f"Service `{external_id}` not found.")
|
|
72
72
|
|
|
73
73
|
router_policy = await mgmt_api.search_service_router_policy(service_name)
|
|
74
74
|
if router_policy:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mrok
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: MPT Extensions OpenZiti Orchestrator
|
|
5
5
|
Author: SoftwareOne AG
|
|
6
6
|
License: Apache License
|
|
@@ -212,14 +212,20 @@ Requires-Dist: dynaconf<4.0.0,>=3.2.11
|
|
|
212
212
|
Requires-Dist: fastapi-pagination<0.15.0,>=0.14.1
|
|
213
213
|
Requires-Dist: fastapi[standard]<0.120.0,>=0.119.0
|
|
214
214
|
Requires-Dist: gunicorn<24.0.0,>=23.0.0
|
|
215
|
+
Requires-Dist: hdrhistogram<0.11.0,>=0.10.3
|
|
215
216
|
Requires-Dist: httptools<0.8.0,>=0.7.1
|
|
216
217
|
Requires-Dist: httpx<0.29.0,>=0.28.1
|
|
217
218
|
Requires-Dist: openziti<2.0.0,>=1.3.1
|
|
219
|
+
Requires-Dist: psutil<8.0.0,>=7.1.3
|
|
218
220
|
Requires-Dist: pydantic<3.0.0,>=2.11.7
|
|
219
221
|
Requires-Dist: pyfiglet<2.0.0,>=1.0.4
|
|
220
222
|
Requires-Dist: pyjwt<3.0.0,>=2.10.1
|
|
223
|
+
Requires-Dist: pytest-textual-snapshot<2.0.0,>=1.1.0
|
|
221
224
|
Requires-Dist: pyyaml<7.0.0,>=6.0.2
|
|
225
|
+
Requires-Dist: pyzmq<28.0.0,>=27.1.0
|
|
222
226
|
Requires-Dist: rich<15.0.0,>=14.1.0
|
|
227
|
+
Requires-Dist: textual-serve<2.0.0,>=1.1.3
|
|
228
|
+
Requires-Dist: textual<7.0.0,>=6.5.0
|
|
223
229
|
Requires-Dist: typer<0.20.0,>=0.19.2
|
|
224
230
|
Requires-Dist: uvicorn-worker<0.5.0,>=0.4.0
|
|
225
231
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
mrok/__init__.py,sha256=D1PUs3KtMCqG4bFLceVNG62L3RN53NS95uSCNXpgvzs,181
|
|
2
|
+
mrok/conf.py,sha256=_5Z-A5LyojQeY8J7W8C0QidsmrPl99r9qKYEoMf4kcI,840
|
|
3
|
+
mrok/datastructures.py,sha256=gp8KF2JoNOxIRzYStVZLKL_XVDbcIVSIDnmpQo4FNt0,4067
|
|
4
|
+
mrok/errors.py,sha256=ruNMDFr2_0ezCGXuCG1OswCEv-bHOIzMMd02J_0ABcs,37
|
|
5
|
+
mrok/logging.py,sha256=ZMWn0w4fJ-F_g-L37H_GM14BSXAIF2mFF_ougX5S7mg,2856
|
|
6
|
+
mrok/master.py,sha256=6_bUic39mXE37lt0PBf_BtY2D2mBR7KzPj65JP7HWPc,8287
|
|
7
|
+
mrok/metrics.py,sha256=asweK_7xiV5MtkDkvbEm9Tktqrl2KHM8VflF0AkNGI0,4036
|
|
8
|
+
mrok/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
mrok/agent/ziticorn.py,sha256=marXGcr6CbDdiNi8V3CZJhg8YCUbKw6ySuO3-0-zf8g,900
|
|
10
|
+
mrok/agent/devtools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
mrok/agent/devtools/__main__.py,sha256=R8ezbW7hCik5r45U3w2TgiTubg9SlbVsWA-bapILJXU,781
|
|
12
|
+
mrok/agent/devtools/inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
mrok/agent/devtools/inspector/__main__.py,sha256=HeYcRf1bjXPji2LKMPCcTU61afrRH2P1RqnFmHClRTc,524
|
|
14
|
+
mrok/agent/devtools/inspector/app.py,sha256=_pzxemMqIunE5EdMq5amjqpOGsMWIOw17GgiCtRAi6Q,16464
|
|
15
|
+
mrok/agent/devtools/inspector/server.py,sha256=C4uD6_1psSHMjJLUDCMPGvKdQYKaEwYTw27NAbwuuA0,636
|
|
16
|
+
mrok/agent/sidecar/__init__.py,sha256=DrjJGhqFyxsVODW06KI20Wpr6HsD2lD6qFCKUXc7GIE,59
|
|
17
|
+
mrok/agent/sidecar/app.py,sha256=1p6qWkXVq78zcJ2dhCYlw8CqfwPsgEtu07Lp5csK3Iw,874
|
|
18
|
+
mrok/agent/sidecar/main.py,sha256=h31wynUCcFmRckvqLHtH97w1QgMv4fzcmYjhRPUobxY,1076
|
|
19
|
+
mrok/cli/__init__.py,sha256=mtFEa8IeS1x6Gm4dUYoSnAxyEzNqbUVSmWxtuZUMR84,61
|
|
20
|
+
mrok/cli/main.py,sha256=DFcYPwDskXi8SKAgEsuP4GMFzaniIf_6bZaSDWvYKDk,2724
|
|
21
|
+
mrok/cli/rich.py,sha256=P3Dyu8EArUR9_0j7DPK7LRx85TWdYdZ1SaJzD_S1ZCE,511
|
|
22
|
+
mrok/cli/utils.py,sha256=m_olScdIUGks5IoC6p2F9D6CQIucWZ7LHyrvwm2bkJw,106
|
|
23
|
+
mrok/cli/commands/__init__.py,sha256=jihISOj3ZZQ_dn0rogXrJOx6b283KLRUfTw9USQgAhI,134
|
|
24
|
+
mrok/cli/commands/admin/__init__.py,sha256=WU49jpMF9p18UONjYywWEFzjF57zLpLKJ0qAZvrzcR4,414
|
|
25
|
+
mrok/cli/commands/admin/bootstrap.py,sha256=iOnHctYajgcHrG_Idjn5Y7VVSaWYRIhdgqKSw9TWq9I,1680
|
|
26
|
+
mrok/cli/commands/admin/utils.py,sha256=wQ-qQJGFyhikMJY_CWT-G6sTEIZb-LUdj1AUZisLPBw,1363
|
|
27
|
+
mrok/cli/commands/admin/list/__init__.py,sha256=kjCMcpn1gopcrQaaHxfFh8Kyngldepnle8R2br5dJ_0,195
|
|
28
|
+
mrok/cli/commands/admin/list/extensions.py,sha256=16fhDB5ucL8su2WQnSaQ1E6MhgC4vkP9-nuHAcPpzyE,4405
|
|
29
|
+
mrok/cli/commands/admin/list/instances.py,sha256=kaqeyidwUxgYqfaHXqp2m76rm5h2ErBsYyZcNeaBRwY,5912
|
|
30
|
+
mrok/cli/commands/admin/register/__init__.py,sha256=5Jb_bc2L47MEpQIrOcquzduTFWQ01Jd1U1MpqaR-Ekw,209
|
|
31
|
+
mrok/cli/commands/admin/register/extensions.py,sha256=p1qX5gSQX1IGpOQjO2MJzbc09v1ebdFuPo94QzJErKk,1485
|
|
32
|
+
mrok/cli/commands/admin/register/instances.py,sha256=XB6uAchc7Rm8uAu7o3-oHaN_rS8CCIBf0QKWZGW86fI,1940
|
|
33
|
+
mrok/cli/commands/admin/unregister/__init__.py,sha256=-GjjCPX1pISbWmJK6GpKO3ijGsDQb21URjU1hNu99O4,215
|
|
34
|
+
mrok/cli/commands/admin/unregister/extensions.py,sha256=GR3Iwzeksk_R0GkgmCSG7iHRcUrI7ABqDi25Gbes64Y,1016
|
|
35
|
+
mrok/cli/commands/admin/unregister/instances.py,sha256=-28wL8pTXTWHVHtw93y8-dqi-Dlf0OZOnlBCKOyGo80,1138
|
|
36
|
+
mrok/cli/commands/agent/__init__.py,sha256=ZAi7eTkKQtfwwV1c1mv3uvEEsyMMrhCQ_-id_0wksAQ,218
|
|
37
|
+
mrok/cli/commands/agent/utils.py,sha256=m_olScdIUGks5IoC6p2F9D6CQIucWZ7LHyrvwm2bkJw,106
|
|
38
|
+
mrok/cli/commands/agent/dev/__init__.py,sha256=ZfreyRuaLqO0AwPS8Ll1DIpsKacsu7_dTmbxV5QecOM,172
|
|
39
|
+
mrok/cli/commands/agent/dev/console.py,sha256=rrKAGoKXVQQBOC75H0JSuX1sYyvc2QSrV-dfMPK49p4,673
|
|
40
|
+
mrok/cli/commands/agent/dev/web.py,sha256=O9dYk-o1FV2E_sKLOezdEmLsnexwbJNDdsYL5pATZRQ,1028
|
|
41
|
+
mrok/cli/commands/agent/run/__init__.py,sha256=E_IJCl3BfMffqFASe8gzJwhhQgt5bQfjhuyekVwdEBA,164
|
|
42
|
+
mrok/cli/commands/agent/run/asgi.py,sha256=dCgzwJtTLv2eyEIP7v1tDfe_PrFBS02SfN5dSDw1Jzg,2054
|
|
43
|
+
mrok/cli/commands/agent/run/sidecar.py,sha256=Tj5inAeSX1E3yCVs2q4P3sP3trvvwk2lYMSUtyFfxo8,2098
|
|
44
|
+
mrok/cli/commands/controller/__init__.py,sha256=2xw-YVN0akiLiuGUU3XbYyZZ0ugOjQ6XhtTkzEKSmMA,161
|
|
45
|
+
mrok/cli/commands/controller/openapi.py,sha256=QLjVao9UkB2vBaGkFi_q_jrlg4Np4ldMRwDIJsrJ7A8,1175
|
|
46
|
+
mrok/cli/commands/controller/run.py,sha256=yl1p7oRHhQINWWjUKlRHtMIWUCV0KsxYdyVyazhX834,2406
|
|
47
|
+
mrok/cli/commands/proxy/__init__.py,sha256=Y9oluemsuWSaykDniLVsI2cyTurcEO3_GJDHgf-7BdU,120
|
|
48
|
+
mrok/cli/commands/proxy/run.py,sha256=QzKAjNCib-SS8IrGGHOxDjTtgTQxfeeeqmI3LaIkiLo,1293
|
|
49
|
+
mrok/controller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
mrok/controller/app.py,sha256=XxCIB7N1YE52vSYfvGW2UPgEEOZ9jxDMe2l9D2SfXi8,1866
|
|
51
|
+
mrok/controller/auth.py,sha256=hYa0OPJ5X0beGxRP6qbxwJOVXj5TmzHjmam2OjTBKn4,2704
|
|
52
|
+
mrok/controller/pagination.py,sha256=raYpYa34q8Ckl4BXBOEdpWlKkFj6z7e6QLWr2HT7dzI,2187
|
|
53
|
+
mrok/controller/schemas.py,sha256=AaF8_bEwZTHM02apVEBAzlUb2t71zoxYaG-VHtPNeMk,1705
|
|
54
|
+
mrok/controller/dependencies/__init__.py,sha256=voewk6gjkA0OarL6HFmfT_RLqBns0Fpl-VIqK5xVAEI,202
|
|
55
|
+
mrok/controller/dependencies/conf.py,sha256=2Pa8fxJHkZ29q6UL-w6hUP_wr7WnNELfw5LlzWg1Tec,162
|
|
56
|
+
mrok/controller/dependencies/ziti.py,sha256=fYoxeJb4s6p2_3gxbExbFSRabjpvp_gZMBb3ocXZV3Y,702
|
|
57
|
+
mrok/controller/openapi/__init__.py,sha256=U1dw45w76CcoQagyqg_FXdMuJF3qJZZM6wG8TeTe3Zo,101
|
|
58
|
+
mrok/controller/openapi/examples.py,sha256=ZI0BP7L6sI0z7Mq1I3uc2UrweGpzpPeGSIuf1bUKkgg,1419
|
|
59
|
+
mrok/controller/openapi/utils.py,sha256=Kn55ISAWlMJNwrJTum7iFrBvJvr81To76pCK8W-s79Q,1114
|
|
60
|
+
mrok/controller/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
+
mrok/controller/routes/extensions.py,sha256=zoY4sNz_BIZcbly6WXM7Rbpn2jmB89njS_0xdJkoKfs,9192
|
|
62
|
+
mrok/controller/routes/instances.py,sha256=v-fn_F6JHbDZ4YUNCIZzClgHp6aC1Eu5HB7k7qBG5pk,2202
|
|
63
|
+
mrok/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
+
mrok/http/config.py,sha256=k73-4hBo6jag1RpyZagJLtTCL6EQoebZaX8Vv-CMN_k,2050
|
|
65
|
+
mrok/http/constants.py,sha256=ao5gI2HFBWmrdd2Yc6XFK_RGaHk-omxI4AqvfIiGes8,409
|
|
66
|
+
mrok/http/forwarder.py,sha256=DakD9hrrCWAzB1B_4SgQaQaEHcDYLLI9WaYs5F0O36I,12977
|
|
67
|
+
mrok/http/lifespan.py,sha256=UdbOqjWZsHzJJjX0CTd2hY96Jpk5QWtdHJEzPG6Z4hQ,1288
|
|
68
|
+
mrok/http/middlewares.py,sha256=SGo4EwhTId2uJx1aMuqGbNy7MXgZlDEdZI0buzBYVv0,5011
|
|
69
|
+
mrok/http/protocol.py,sha256=ap8jbLUvgbAH81ZJZCBkQiYR7mkV_eL3rpfwEkoE8sU,392
|
|
70
|
+
mrok/http/server.py,sha256=Mj7C85fc-DXp-WTBWaOd7ag808oliLmFBH5bf-G2FHg,370
|
|
71
|
+
mrok/http/types.py,sha256=XpNrvbfpANKvmjOBYtLF1FmDHoJF3z_MIMQHXoJlvmE,1302
|
|
72
|
+
mrok/http/utils.py,sha256=sOixYu3R9-nNoMFYdifrreYvcFRIHYVtb6AAmtVzaLE,2125
|
|
73
|
+
mrok/proxy/__init__.py,sha256=vWXyImroqM1Eq8e_oFPBup8VJ3reyp8SVjFTbLzRkI8,51
|
|
74
|
+
mrok/proxy/app.py,sha256=MwJZ91MQ1oWkbmsX7-NooZSV9BkLVZPFmUKpNPC4HHQ,2490
|
|
75
|
+
mrok/proxy/dataclasses.py,sha256=DtX-Yuma-uOECOPefJnoQJhZMEtT6Za_27cd-lJE9Iw,237
|
|
76
|
+
mrok/proxy/main.py,sha256=ZXpticE6J4FABaslDB_8J5qklPsf3e7xIFSZmcPAAjQ,1588
|
|
77
|
+
mrok/proxy/streams.py,sha256=6TMZwrQPbSyQqpqavsoTeyUmS2O026pJfiCnxLopPqg,3425
|
|
78
|
+
mrok/proxy/types.py,sha256=dgWqAj6dFGVH_Q8-k8sU5h18yoUF_fTn-SRPIfEs_gA,308
|
|
79
|
+
mrok/proxy/ziti.py,sha256=dKd6UzmEAFu9-gey871sPEDUZTkt4YVPyCYRzeA5mlA,6539
|
|
80
|
+
mrok/ziti/__init__.py,sha256=20OWMiexRhOovZOX19zlX87-V78QyWnEnSZfyAftUdE,263
|
|
81
|
+
mrok/ziti/api.py,sha256=KvGiT9d4oSgC3JbFWLDQyuHcLX2HuZJoJ8nHmWtCDkY,16154
|
|
82
|
+
mrok/ziti/bootstrap.py,sha256=QIDhlkIxPW2QRuumFq2D1WDbD003P5f3z24pAUsyeBI,2696
|
|
83
|
+
mrok/ziti/constants.py,sha256=Urq1X3bCBQZfw8NbnEa1pqmY4oq1wmzkwPfzam3kbTw,339
|
|
84
|
+
mrok/ziti/errors.py,sha256=yYCbVDwktnR0AYduqtynIjo73K3HOhIrwA_vQimvEd4,368
|
|
85
|
+
mrok/ziti/identities.py,sha256=1BcwfqAJHMBhc3vRaf0aLaIkoHskj5Xe2Lsq2lO9Vs8,6735
|
|
86
|
+
mrok/ziti/pki.py,sha256=o2tySqHC8-7bvFuI2Tqxg9vX6H6ZSxWxfP_9x29e19M,1954
|
|
87
|
+
mrok/ziti/services.py,sha256=zR1PEBYwXVou20iJK4euh0ZZFAo9UB8PZk8f6SDmiUE,3194
|
|
88
|
+
mrok-0.4.0.dist-info/METADATA,sha256=39Su41kvqC4oAsQl1_ZG7b5v1xRr6c2YK6x84XgURi8,15796
|
|
89
|
+
mrok-0.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
90
|
+
mrok-0.4.0.dist-info/entry_points.txt,sha256=tloXwvU1uJicBJR2h-8HoVclPgwJWDwuREMHN8Zq-nU,38
|
|
91
|
+
mrok-0.4.0.dist-info/licenses/LICENSE.txt,sha256=6PaICaoA3yNsZKLv5G6OKqSfLSoX7MakYqTDgJoTCBs,11346
|
|
92
|
+
mrok-0.4.0.dist-info/RECORD,,
|