mrok 0.4.2__tar.gz → 0.4.4__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.
- {mrok-0.4.2 → mrok-0.4.4}/PKG-INFO +1 -1
- {mrok-0.4.2 → mrok-0.4.4}/mrok/proxy/app.py +7 -12
- mrok-0.4.4/mrok/proxy/ziti.py +102 -0
- {mrok-0.4.2 → mrok-0.4.4}/pyproject.toml +1 -1
- mrok-0.4.2/mrok/proxy/dataclasses.py +0 -12
- mrok-0.4.2/mrok/proxy/streams.py +0 -124
- mrok-0.4.2/mrok/proxy/types.py +0 -11
- mrok-0.4.2/mrok/proxy/ziti.py +0 -117
- {mrok-0.4.2 → mrok-0.4.4}/.github/actions/setup-python-env/action.yml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.github/workflows/assets/turing_team_pr_bot.png +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.github/workflows/notify-pr-closed.yaml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.github/workflows/notify-pr-reviewed.yml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.github/workflows/pr-build-merge.yaml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.github/workflows/release.yml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.gitignore +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.pre-commit-config.yaml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/.python-version +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/LICENSE.txt +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/README.md +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/dev.Dockerfile +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/docker-compose.yaml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/entrypoint.sh +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/devtools/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/devtools/__main__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/devtools/inspector/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/devtools/inspector/__main__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/devtools/inspector/app.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/devtools/inspector/server.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/sidecar/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/sidecar/app.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/sidecar/main.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/agent/ziticorn.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/bootstrap.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/list/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/list/extensions.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/list/instances.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/register/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/register/extensions.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/register/instances.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/unregister/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/unregister/extensions.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/unregister/instances.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/admin/utils.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/dev/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/dev/console.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/dev/web.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/run/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/run/asgi.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/run/sidecar.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/agent/utils.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/controller/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/controller/openapi.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/controller/run.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/proxy/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/commands/proxy/run.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/main.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/rich.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/cli/utils.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/conf.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/app.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/auth.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/dependencies/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/dependencies/conf.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/dependencies/ziti.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/openapi/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/openapi/examples.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/openapi/utils.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/pagination.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/routes/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/routes/extensions.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/routes/instances.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/controller/schemas.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/datastructures.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/errors.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/config.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/constants.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/forwarder.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/lifespan.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/middlewares.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/protocol.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/server.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/types.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/http/utils.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/logging.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/master.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/metrics.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/proxy/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/proxy/main.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/api.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/bootstrap.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/constants.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/errors.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/identities.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/pki.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/mrok/ziti/services.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/prod.Dockerfile +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/scripts/ziti.sh +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/settings.yaml +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/snapshot_report.html +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/sonar-project.properties +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/__snapshots__/test_inspector/test_inspector_app.svg +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/__snapshots__/test_inspector/test_inspector_app_empty_card.svg +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/__snapshots__/test_inspector/test_inspector_app_filed_store_connection.svg +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/__snapshots__/test_inspector/test_inspector_app_open_card.svg +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/test_app.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/test_main.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/agent/test_ziticorn.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/admin/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/admin/test_bootstrap.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/admin/test_list.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/admin/test_register.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/admin/test_unregister.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/admin/test_utils.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/agent/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/agent/test_run.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/controller/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/controller/test_openapi.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/controller/test_run.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/proxy/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/proxy/test_run.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/cli/test_main.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/conftest.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/controller/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/controller/test_auth.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/controller/test_extensions.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/controller/test_instances.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/controller/test_openapi.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/test_config.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/test_forwarder.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/test_lifespan.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/test_master.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/test_protocol.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/http/test_server.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/proxy/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/proxy/test_app.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/proxy/test_ziti.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/proxy/test_ziti_branches.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/types.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/ziti/__init__.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/ziti/test_api.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/ziti/test_bootstrap.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/ziti/test_identities.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/ziti/test_pki.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/tests/ziti/test_services.py +0 -0
- {mrok-0.4.2 → mrok-0.4.4}/uv.lock +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import logging
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
@@ -5,7 +6,7 @@ from mrok.conf import get_settings
|
|
|
5
6
|
from mrok.http.forwarder import ForwardAppBase
|
|
6
7
|
from mrok.http.types import Scope, StreamReader, StreamWriter
|
|
7
8
|
from mrok.logging import setup_logging
|
|
8
|
-
from mrok.proxy.ziti import
|
|
9
|
+
from mrok.proxy.ziti import ZitiSocketCache
|
|
9
10
|
|
|
10
11
|
logger = logging.getLogger("mrok.proxy")
|
|
11
12
|
|
|
@@ -20,8 +21,6 @@ class ProxyApp(ForwardAppBase):
|
|
|
20
21
|
identity_file: str | Path,
|
|
21
22
|
*,
|
|
22
23
|
read_chunk_size: int = 65536,
|
|
23
|
-
ziti_connection_ttl_seconds: float = 60,
|
|
24
|
-
ziti_conn_cache_purge_interval_seconds: float = 10,
|
|
25
24
|
) -> None:
|
|
26
25
|
super().__init__(read_chunk_size=read_chunk_size)
|
|
27
26
|
self._identity_file = identity_file
|
|
@@ -31,11 +30,7 @@ class ProxyApp(ForwardAppBase):
|
|
|
31
30
|
if settings.proxy.domain[0] == "."
|
|
32
31
|
else f".{settings.proxy.domain}"
|
|
33
32
|
)
|
|
34
|
-
self.
|
|
35
|
-
identity_file,
|
|
36
|
-
ttl_seconds=ziti_connection_ttl_seconds,
|
|
37
|
-
cleanup_interval=ziti_conn_cache_purge_interval_seconds,
|
|
38
|
-
)
|
|
33
|
+
self._ziti_socket_cache = ZitiSocketCache(self._identity_file)
|
|
39
34
|
|
|
40
35
|
def get_target_from_header(self, headers: dict[str, str], name: str) -> str | None:
|
|
41
36
|
header_value = headers.get(name, "")
|
|
@@ -54,10 +49,9 @@ class ProxyApp(ForwardAppBase):
|
|
|
54
49
|
|
|
55
50
|
async def startup(self):
|
|
56
51
|
setup_logging(get_settings())
|
|
57
|
-
await self._conn_manager.start()
|
|
58
52
|
|
|
59
53
|
async def shutdown(self):
|
|
60
|
-
await self.
|
|
54
|
+
await self._ziti_socket_cache.stop()
|
|
61
55
|
|
|
62
56
|
async def select_backend(
|
|
63
57
|
self,
|
|
@@ -65,5 +59,6 @@ class ProxyApp(ForwardAppBase):
|
|
|
65
59
|
headers: dict[str, str],
|
|
66
60
|
) -> tuple[StreamReader, StreamWriter] | tuple[None, None]:
|
|
67
61
|
target_name = self.get_target_name(headers)
|
|
68
|
-
|
|
69
|
-
|
|
62
|
+
sock = await self._ziti_socket_cache.get_or_create(target_name)
|
|
63
|
+
reader, writer = await asyncio.open_connection(sock=sock)
|
|
64
|
+
return reader, writer
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import contextlib
|
|
3
|
+
import logging
|
|
4
|
+
from asyncio import Task
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import openziti
|
|
8
|
+
from aiocache import Cache
|
|
9
|
+
from openziti.context import ZitiContext
|
|
10
|
+
from openziti.zitisock import ZitiSocket
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("mrok.proxy")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ZitiSocketCache:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
identity_file: str | Path,
|
|
19
|
+
ziti_ctx_timeout_ms: int = 10_000,
|
|
20
|
+
ttl_seconds: float = 60.0,
|
|
21
|
+
cleanup_interval: float = 10.0,
|
|
22
|
+
) -> None:
|
|
23
|
+
self._identity_file = identity_file
|
|
24
|
+
self._ziti_ctx_timeout_ms = ziti_ctx_timeout_ms
|
|
25
|
+
self._ttl_seconds = ttl_seconds
|
|
26
|
+
self._cleanup_interval = cleanup_interval
|
|
27
|
+
|
|
28
|
+
self._ziti_ctx: ZitiContext | None = None
|
|
29
|
+
self._cache = Cache(Cache.MEMORY)
|
|
30
|
+
self._active_sockets: dict[str, ZitiSocket] = {}
|
|
31
|
+
self._cleanup_task: Task | None = None
|
|
32
|
+
|
|
33
|
+
def _get_ziti_ctx(self) -> ZitiContext:
|
|
34
|
+
if self._ziti_ctx is None:
|
|
35
|
+
ctx, err = openziti.load(str(self._identity_file), timeout=self._ziti_ctx_timeout_ms)
|
|
36
|
+
if err != 0:
|
|
37
|
+
raise Exception(f"Cannot create a Ziti context from the identity file: {err}")
|
|
38
|
+
self._ziti_ctx = ctx
|
|
39
|
+
return self._ziti_ctx
|
|
40
|
+
|
|
41
|
+
async def _create_socket(self, key: str):
|
|
42
|
+
return self._get_ziti_ctx().connect(key)
|
|
43
|
+
|
|
44
|
+
async def get_or_create(self, key: str):
|
|
45
|
+
sock = await self._cache.get(key)
|
|
46
|
+
|
|
47
|
+
if sock:
|
|
48
|
+
await self._cache.expire(key, self._ttl_seconds)
|
|
49
|
+
self._active_sockets[key] = sock
|
|
50
|
+
logger.debug(f"Ziti socket found for service {key}")
|
|
51
|
+
return sock
|
|
52
|
+
|
|
53
|
+
sock = await self._create_socket(key)
|
|
54
|
+
await self._cache.set(key, sock, self._ttl_seconds)
|
|
55
|
+
self._active_sockets[key] = sock
|
|
56
|
+
logger.info(f"New Ziti socket created for service {key}")
|
|
57
|
+
return sock
|
|
58
|
+
|
|
59
|
+
# async def invalidate(self, key: str):
|
|
60
|
+
# sock = await self._cache.get(key)
|
|
61
|
+
# if sock:
|
|
62
|
+
# await self._close_socket(sock)
|
|
63
|
+
|
|
64
|
+
# await self._cache.delete(key)
|
|
65
|
+
# self._active_sockets.pop(key, None)
|
|
66
|
+
|
|
67
|
+
async def start(self):
|
|
68
|
+
self._cleanup_task = asyncio.create_task(self._periodic_cleanup())
|
|
69
|
+
# Warmup ziti context
|
|
70
|
+
self._get_ziti_ctx()
|
|
71
|
+
|
|
72
|
+
async def stop(self):
|
|
73
|
+
self._cleanup_task.cancel()
|
|
74
|
+
with contextlib.suppress(Exception):
|
|
75
|
+
await self._cleanup_task
|
|
76
|
+
|
|
77
|
+
for sock in list(self._active_sockets.values()):
|
|
78
|
+
await self._close_socket(sock)
|
|
79
|
+
|
|
80
|
+
self._active_sockets.clear()
|
|
81
|
+
await self._cache.clear()
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
async def _close_socket(sock: ZitiSocket):
|
|
85
|
+
with contextlib.suppress(Exception):
|
|
86
|
+
sock.close()
|
|
87
|
+
|
|
88
|
+
async def _periodic_cleanup(self):
|
|
89
|
+
try:
|
|
90
|
+
while True:
|
|
91
|
+
await asyncio.sleep(self._cleanup_interval)
|
|
92
|
+
await self._cleanup_once()
|
|
93
|
+
except asyncio.CancelledError:
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
async def _cleanup_once(self):
|
|
97
|
+
expired = {key for key in self._active_sockets.keys() if not self._cache.exists(key)}
|
|
98
|
+
for key in expired:
|
|
99
|
+
logger.debug(f"Cleaning up expired socket connection {key}")
|
|
100
|
+
sock = self._active_sockets.pop(key, None)
|
|
101
|
+
if sock:
|
|
102
|
+
await self._close_socket(sock)
|
mrok-0.4.2/mrok/proxy/streams.py
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from mrok.proxy.types import ConnectionCache
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class CachedStreamReader:
|
|
7
|
-
def __init__(
|
|
8
|
-
self,
|
|
9
|
-
reader: asyncio.StreamReader,
|
|
10
|
-
key: str,
|
|
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: str,
|
|
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-0.4.2/mrok/proxy/types.py
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Protocol
|
|
4
|
-
|
|
5
|
-
from mrok.http.types import StreamReader, StreamWriter
|
|
6
|
-
|
|
7
|
-
StreamPair = tuple[StreamReader, StreamWriter]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class ConnectionCache(Protocol):
|
|
11
|
-
async def invalidate(self, key: str) -> None: ...
|
mrok-0.4.2/mrok/proxy/ziti.py
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import contextlib
|
|
3
|
-
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import openziti
|
|
7
|
-
from aiocache import Cache
|
|
8
|
-
|
|
9
|
-
from mrok.proxy.streams import CachedStreamReader, CachedStreamWriter
|
|
10
|
-
from mrok.proxy.types import StreamPair
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger("mrok.proxy")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class ZitiConnectionManager:
|
|
16
|
-
def __init__(
|
|
17
|
-
self,
|
|
18
|
-
identity_file: str | Path,
|
|
19
|
-
ziti_timeout_ms: int = 10000,
|
|
20
|
-
ttl_seconds: float = 60.0,
|
|
21
|
-
cleanup_interval: float = 10.0,
|
|
22
|
-
):
|
|
23
|
-
self.identity_file = identity_file
|
|
24
|
-
self.ziti_timeout_ms = ziti_timeout_ms
|
|
25
|
-
self.ttl_seconds = ttl_seconds
|
|
26
|
-
self.cleanup_interval = cleanup_interval
|
|
27
|
-
|
|
28
|
-
self.cache = Cache(Cache.MEMORY)
|
|
29
|
-
|
|
30
|
-
self._active_pairs: dict[str, StreamPair] = {}
|
|
31
|
-
|
|
32
|
-
self._cleanup_task: asyncio.Task | None = None
|
|
33
|
-
self._ziti_ctx: openziti.context.ZitiContext | None = None
|
|
34
|
-
|
|
35
|
-
async def create_stream_pair(self, key: str) -> StreamPair:
|
|
36
|
-
if not self._ziti_ctx:
|
|
37
|
-
raise Exception("ZitiConnectionManager is not started")
|
|
38
|
-
sock = self._ziti_ctx.connect(key)
|
|
39
|
-
orig_reader, orig_writer = await asyncio.open_connection(sock=sock)
|
|
40
|
-
|
|
41
|
-
reader = CachedStreamReader(orig_reader, key, self)
|
|
42
|
-
writer = CachedStreamWriter(orig_writer, key, self)
|
|
43
|
-
return (reader, writer)
|
|
44
|
-
|
|
45
|
-
async def get_or_create(self, key: str) -> StreamPair:
|
|
46
|
-
pair = await self.cache.get(key)
|
|
47
|
-
|
|
48
|
-
if pair:
|
|
49
|
-
logger.info(f"return cached connection for {key}")
|
|
50
|
-
await self.cache.set(key, pair, ttl=self.ttl_seconds)
|
|
51
|
-
self._active_pairs[key] = pair
|
|
52
|
-
return pair
|
|
53
|
-
|
|
54
|
-
pair = await self.create_stream_pair(key)
|
|
55
|
-
await self.cache.set(key, pair, ttl=self.ttl_seconds)
|
|
56
|
-
self._active_pairs[key] = pair
|
|
57
|
-
logger.info(f"return new connection for {key}")
|
|
58
|
-
return pair
|
|
59
|
-
|
|
60
|
-
async def invalidate(self, key: str) -> None:
|
|
61
|
-
logger.info(f"invalidating connection for {key}")
|
|
62
|
-
pair = await self.cache.get(key)
|
|
63
|
-
if pair:
|
|
64
|
-
await self._close_pair(pair)
|
|
65
|
-
|
|
66
|
-
await self.cache.delete(key)
|
|
67
|
-
self._active_pairs.pop(key, None)
|
|
68
|
-
|
|
69
|
-
async def start(self) -> None:
|
|
70
|
-
if self._cleanup_task is None:
|
|
71
|
-
self._cleanup_task = asyncio.create_task(self._periodic_cleanup())
|
|
72
|
-
if self._ziti_ctx is None:
|
|
73
|
-
ctx, err = openziti.load(str(self.identity_file), timeout=self.ziti_timeout_ms)
|
|
74
|
-
if err != 0:
|
|
75
|
-
raise Exception(f"Cannot create a Ziti context from the identity file: {err}")
|
|
76
|
-
self._ziti_ctx = ctx
|
|
77
|
-
|
|
78
|
-
async def stop(self) -> None:
|
|
79
|
-
if self._cleanup_task:
|
|
80
|
-
self._cleanup_task.cancel()
|
|
81
|
-
with contextlib.suppress(Exception):
|
|
82
|
-
await self._cleanup_task
|
|
83
|
-
|
|
84
|
-
for pair in list(self._active_pairs.values()):
|
|
85
|
-
await self._close_pair(pair)
|
|
86
|
-
|
|
87
|
-
self._active_pairs.clear()
|
|
88
|
-
await self.cache.clear()
|
|
89
|
-
openziti.shutdown()
|
|
90
|
-
|
|
91
|
-
@staticmethod
|
|
92
|
-
async def _close_pair(pair: StreamPair) -> None:
|
|
93
|
-
reader, writer = pair
|
|
94
|
-
writer.close()
|
|
95
|
-
with contextlib.suppress(Exception):
|
|
96
|
-
await writer.wait_closed()
|
|
97
|
-
|
|
98
|
-
async def _periodic_cleanup(self) -> None:
|
|
99
|
-
try:
|
|
100
|
-
while True:
|
|
101
|
-
await asyncio.sleep(self.cleanup_interval)
|
|
102
|
-
await self._cleanup_once()
|
|
103
|
-
except asyncio.CancelledError:
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
async def _cleanup_once(self) -> None:
|
|
107
|
-
# Keys currently stored in aiocache
|
|
108
|
-
keys_in_cache = set(await self.cache.keys())
|
|
109
|
-
# Keys we think are alive
|
|
110
|
-
known_keys = set(self._active_pairs.keys())
|
|
111
|
-
|
|
112
|
-
expired_keys = known_keys - keys_in_cache
|
|
113
|
-
|
|
114
|
-
for key in expired_keys:
|
|
115
|
-
pair = self._active_pairs.pop(key, None)
|
|
116
|
-
if pair:
|
|
117
|
-
await self._close_pair(pair)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mrok-0.4.2 → mrok-0.4.4}/tests/agent/sidecar/__snapshots__/test_inspector/test_inspector_app.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|