mrok 0.1.8__tar.gz → 0.2.0__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.1.8 → mrok-0.2.0}/.github/workflows/pr-build-merge.yaml +2 -2
- {mrok-0.1.8 → mrok-0.2.0}/PKG-INFO +1 -1
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/app.py +3 -1
- mrok-0.2.0/mrok/controller/routes/instances.py +71 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/api.py +3 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/bootstrap.py +9 -4
- {mrok-0.1.8 → mrok-0.2.0}/pyproject.toml +1 -1
- mrok-0.1.8/tests/controller/test_instances.py → mrok-0.2.0/tests/controller/test_extensions.py +228 -10
- mrok-0.2.0/tests/controller/test_instances.py +157 -0
- mrok-0.2.0/tests/ziti/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/ziti/test_api.py +1 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/ziti/test_bootstrap.py +40 -0
- mrok-0.1.8/tests/controller/test_extensions.py +0 -221
- {mrok-0.1.8 → mrok-0.2.0}/.github/actions/setup-python-env/action.yml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.github/workflows/assets/turing_team_pr_bot.png +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.github/workflows/notify-pr-closed.yaml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.github/workflows/notify-pr-reviewed.yml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.github/workflows/release.yml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.gitignore +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.pre-commit-config.yaml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/.python-version +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/LICENSE.txt +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/README.md +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/dev.Dockerfile +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/docker-compose.yaml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/entrypoint.sh +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/agent/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/agent/sidecar/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/agent/sidecar/app.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/agent/sidecar/main.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/agent/ziticorn.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/bootstrap.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/list/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/list/extensions.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/list/instances.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/register/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/register/extensions.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/register/instances.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/unregister/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/unregister/extensions.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/unregister/instances.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/admin/utils.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/agent/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/agent/run/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/agent/run/asgi.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/agent/run/sidecar.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/controller/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/controller/openapi.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/commands/controller/run.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/main.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/cli/rich.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/conf.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/auth.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/dependencies/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/dependencies/conf.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/dependencies/ziti.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/openapi/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/openapi/examples.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/openapi/utils.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/pagination.py +0 -0
- {mrok-0.1.8/mrok/http → mrok-0.2.0/mrok/controller/routes}/__init__.py +0 -0
- /mrok-0.1.8/mrok/controller/routes.py → /mrok-0.2.0/mrok/controller/routes/extensions.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/controller/schemas.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/errors.py +0 -0
- {mrok-0.1.8/tests → mrok-0.2.0/mrok/http}/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/http/config.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/http/forwarder.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/http/lifespan.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/http/master.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/http/protocol.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/http/server.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/logging.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/constants.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/errors.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/identities.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/pki.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/mrok/ziti/services.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/prod.Dockerfile +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/scripts/ziti.sh +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/settings.yaml +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/sonar-project.properties +0 -0
- {mrok-0.1.8/tests/agent → mrok-0.2.0/tests}/__init__.py +0 -0
- {mrok-0.1.8/tests/agent/sidecar → mrok-0.2.0/tests/agent}/__init__.py +0 -0
- {mrok-0.1.8/tests/cli → mrok-0.2.0/tests/agent/sidecar}/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/agent/sidecar/test_app.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/agent/sidecar/test_main.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/agent/test_ziticorn.py +0 -0
- {mrok-0.1.8/tests/cli/admin → mrok-0.2.0/tests/cli}/__init__.py +0 -0
- {mrok-0.1.8/tests/cli/agent → mrok-0.2.0/tests/cli/admin}/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/admin/test_bootstrap.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/admin/test_list.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/admin/test_register.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/admin/test_unregister.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/admin/test_utils.py +0 -0
- {mrok-0.1.8/tests/cli/controller → mrok-0.2.0/tests/cli/agent}/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/agent/test_run.py +0 -0
- {mrok-0.1.8/tests → mrok-0.2.0/tests/cli}/controller/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/controller/test_openapi.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/controller/test_run.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/cli/test_main.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/conftest.py +0 -0
- {mrok-0.1.8/tests/http → mrok-0.2.0/tests/controller}/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/controller/test_auth.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/controller/test_openapi.py +0 -0
- {mrok-0.1.8/tests/ziti → mrok-0.2.0/tests/http}/__init__.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/http/test_config.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/http/test_forwarder.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/http/test_lifespan.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/http/test_master.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/http/test_protocol.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/http/test_server.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/ziti/test_identities.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/ziti/test_pki.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/tests/ziti/test_services.py +0 -0
- {mrok-0.1.8 → mrok-0.2.0}/uv.lock +0 -0
|
@@ -38,7 +38,7 @@ jobs:
|
|
|
38
38
|
with:
|
|
39
39
|
python-version: ${{ matrix.python-version }}
|
|
40
40
|
|
|
41
|
-
- name: Install the project
|
|
41
|
+
- name: Install the project dependencies
|
|
42
42
|
run: uv sync --all-extras --dev
|
|
43
43
|
|
|
44
44
|
- name: Run formatting checks
|
|
@@ -77,7 +77,7 @@ jobs:
|
|
|
77
77
|
with:
|
|
78
78
|
python-version-file: ".python-version"
|
|
79
79
|
|
|
80
|
-
- name: Install the project
|
|
80
|
+
- name: Install the project dependencies
|
|
81
81
|
run: uv sync --all-extras --dev
|
|
82
82
|
|
|
83
83
|
- name: Run tests
|
|
@@ -8,7 +8,8 @@ from fastapi.routing import APIRoute, APIRouter
|
|
|
8
8
|
from mrok.conf import get_settings
|
|
9
9
|
from mrok.controller.auth import authenticate
|
|
10
10
|
from mrok.controller.openapi import generate_openapi_spec
|
|
11
|
-
from mrok.controller.routes import router as extensions_router
|
|
11
|
+
from mrok.controller.routes.extensions import router as extensions_router
|
|
12
|
+
from mrok.controller.routes.instances import router as instances_router
|
|
12
13
|
|
|
13
14
|
logger = logging.getLogger(__name__)
|
|
14
15
|
|
|
@@ -52,6 +53,7 @@ def setup_app():
|
|
|
52
53
|
app.include_router(
|
|
53
54
|
extensions_router, prefix="/extensions", dependencies=[Depends(authenticate)]
|
|
54
55
|
)
|
|
56
|
+
app.include_router(instances_router, prefix="/instances", dependencies=[Depends(authenticate)])
|
|
55
57
|
|
|
56
58
|
settings = get_settings()
|
|
57
59
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException, status
|
|
4
|
+
|
|
5
|
+
from mrok.controller.dependencies import ZitiManagementAPI
|
|
6
|
+
from mrok.controller.openapi import examples
|
|
7
|
+
from mrok.controller.pagination import LimitOffsetPage, paginate
|
|
8
|
+
from mrok.controller.schemas import InstanceRead
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("mrok.controller")
|
|
11
|
+
|
|
12
|
+
router = APIRouter()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def fetch_instance_or_404(mgmt_api: ZitiManagementAPI, id_or_instance_id: str):
|
|
16
|
+
identity = await mgmt_api.search_identity(id_or_instance_id)
|
|
17
|
+
if not identity:
|
|
18
|
+
raise HTTPException(
|
|
19
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
20
|
+
)
|
|
21
|
+
return identity
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@router.get(
|
|
25
|
+
"/{id_or_instance_id}",
|
|
26
|
+
response_model=InstanceRead,
|
|
27
|
+
responses={
|
|
28
|
+
200: {
|
|
29
|
+
"description": "Instance",
|
|
30
|
+
"content": {"application/json": {"example": examples.INSTANCE_RESPONSE}},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
dependencies=[],
|
|
34
|
+
tags=["Instances"],
|
|
35
|
+
)
|
|
36
|
+
async def get_instance_by_id_or_instance_id(
|
|
37
|
+
mgmt_api: ZitiManagementAPI,
|
|
38
|
+
id_or_instance_id: str,
|
|
39
|
+
):
|
|
40
|
+
identity = await fetch_instance_or_404(mgmt_api, id_or_instance_id)
|
|
41
|
+
return InstanceRead(**identity)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@router.get(
|
|
45
|
+
"",
|
|
46
|
+
response_model=LimitOffsetPage[InstanceRead],
|
|
47
|
+
responses={
|
|
48
|
+
200: {
|
|
49
|
+
"description": "List of Instances",
|
|
50
|
+
"content": {
|
|
51
|
+
"application/json": {
|
|
52
|
+
"example": {
|
|
53
|
+
"data": [examples.INSTANCE_RESPONSE],
|
|
54
|
+
"$meta": {
|
|
55
|
+
"pagination": {
|
|
56
|
+
"total": 1,
|
|
57
|
+
"limit": 10,
|
|
58
|
+
"offset": 0,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
tags=["Instances"],
|
|
67
|
+
)
|
|
68
|
+
async def get_instances(
|
|
69
|
+
mgmt_api: ZitiManagementAPI,
|
|
70
|
+
):
|
|
71
|
+
return await paginate(mgmt_api, "/identities", InstanceRead)
|
|
@@ -397,6 +397,9 @@ class ZitiManagementAPI(BaseZitiAPI):
|
|
|
397
397
|
async def search_config_type(self, id_or_name: str) -> dict[str, Any] | None:
|
|
398
398
|
return await self.search_by_id_or_name("/config-types", id_or_name)
|
|
399
399
|
|
|
400
|
+
async def delete_config_type(self, config_type_id: str) -> dict[str, Any] | None:
|
|
401
|
+
return await self.delete("/config-types", config_type_id)
|
|
402
|
+
|
|
400
403
|
async def get_identity(self, identity_id: str) -> dict[str, Any]:
|
|
401
404
|
return await self.get("/identities", identity_id)
|
|
402
405
|
|
|
@@ -19,19 +19,27 @@ async def bootstrap_identity(
|
|
|
19
19
|
|
|
20
20
|
identity_json = None
|
|
21
21
|
existing_identity = await mgmt_api.search_identity(identity_name)
|
|
22
|
+
policy = await mgmt_api.search_router_policy(identity_name)
|
|
23
|
+
config_type_name = f"{mode}.proxy.v1"
|
|
24
|
+
config_type = await mgmt_api.search_config_type(config_type_name)
|
|
22
25
|
|
|
23
26
|
if forced and existing_identity:
|
|
24
27
|
logger.info(f"Deleting existing identity '{identity_name}' ({existing_identity['id']})")
|
|
25
28
|
|
|
26
|
-
policy = await mgmt_api.search_router_policy(identity_name)
|
|
27
29
|
if policy:
|
|
28
30
|
await mgmt_api.delete_router_policy(policy["id"])
|
|
29
31
|
logger.info(f"Deleted existing ERP '{policy['name']}' ({policy['id']})")
|
|
32
|
+
policy = None
|
|
30
33
|
|
|
31
34
|
await mgmt_api.delete_identity(existing_identity["id"])
|
|
32
35
|
logger.info("Deleted existing identity")
|
|
33
36
|
existing_identity = None
|
|
34
37
|
|
|
38
|
+
if forced and config_type:
|
|
39
|
+
await mgmt_api.delete_config_type(config_type["id"])
|
|
40
|
+
logger.info(f"Deleted existing config type '{config_type_name}' ({config_type['id']})")
|
|
41
|
+
config_type = None
|
|
42
|
+
|
|
35
43
|
if existing_identity:
|
|
36
44
|
frontend_id = existing_identity["id"]
|
|
37
45
|
logger.info(f"Identity '{identity_name}' ({frontend_id}) is already enrolled")
|
|
@@ -44,7 +52,6 @@ async def bootstrap_identity(
|
|
|
44
52
|
)
|
|
45
53
|
logger.info(f"Identity '{identity_name}' ({frontend_id}) successfully enrolled")
|
|
46
54
|
|
|
47
|
-
policy = await mgmt_api.search_router_policy(identity_name)
|
|
48
55
|
if not policy:
|
|
49
56
|
policy_id = await mgmt_api.create_router_policy(
|
|
50
57
|
identity_name,
|
|
@@ -55,8 +62,6 @@ async def bootstrap_identity(
|
|
|
55
62
|
else:
|
|
56
63
|
logger.info(f"Found ERP '{policy['name']}' ({policy['id']})")
|
|
57
64
|
|
|
58
|
-
config_type_name = f"{mode}.proxy.v1"
|
|
59
|
-
config_type = await mgmt_api.search_config_type(config_type_name)
|
|
60
65
|
if config_type is None:
|
|
61
66
|
config_type_id = await mgmt_api.create_config_type(config_type_name, tags=tags)
|
|
62
67
|
logger.info(f"Created '{config_type_name}' ({config_type_id}) config type")
|
mrok-0.1.8/tests/controller/test_instances.py → mrok-0.2.0/tests/controller/test_extensions.py
RENAMED
|
@@ -1,15 +1,227 @@
|
|
|
1
1
|
from urllib.parse import quote
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from dynaconf.base import LazySettings
|
|
4
5
|
from httpx import AsyncClient
|
|
5
6
|
from pytest_httpx import HTTPXMock
|
|
6
7
|
from pytest_mock import MockerFixture
|
|
7
8
|
|
|
8
9
|
from mrok.ziti.api import ZitiClientAPI, ZitiManagementAPI
|
|
9
10
|
from mrok.ziti.constants import MROK_SERVICE_TAG_NAME, MROK_VERSION_TAG_NAME
|
|
11
|
+
from mrok.ziti.errors import (
|
|
12
|
+
ConfigTypeNotFoundError,
|
|
13
|
+
MrokError,
|
|
14
|
+
ProxyIdentityNotFoundError,
|
|
15
|
+
ServiceAlreadyRegisteredError,
|
|
16
|
+
ServiceNotFoundError,
|
|
17
|
+
)
|
|
10
18
|
from tests.conftest import SettingsFactory
|
|
11
19
|
|
|
12
20
|
|
|
21
|
+
@pytest.mark.asyncio
|
|
22
|
+
async def test_list_extensions(
|
|
23
|
+
api_client: AsyncClient, settings_factory: SettingsFactory, httpx_mock: HTTPXMock
|
|
24
|
+
):
|
|
25
|
+
settings = settings_factory()
|
|
26
|
+
httpx_mock.add_response(
|
|
27
|
+
method="GET",
|
|
28
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/services?limit=5&offset=0",
|
|
29
|
+
json={
|
|
30
|
+
"meta": {"pagination": {"totalCount": 10, "limit": 5, "offset": 0}},
|
|
31
|
+
"data": [{"id": f"svc{i}", "name": "svc"} for i in range(5)],
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
httpx_mock.add_response(
|
|
35
|
+
method="GET",
|
|
36
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/services?limit=5&offset=5",
|
|
37
|
+
json={
|
|
38
|
+
"meta": {"pagination": {"totalCount": 10, "limit": 5, "offset": 5}},
|
|
39
|
+
"data": [{"id": f"svc{5 + i}", "name": "svc"} for i in range(5)],
|
|
40
|
+
},
|
|
41
|
+
)
|
|
42
|
+
response = await api_client.get("/extensions?limit=5&offset=0")
|
|
43
|
+
assert response.status_code == 200
|
|
44
|
+
page_1 = response.json()
|
|
45
|
+
assert len(page_1["data"]) == 5
|
|
46
|
+
assert page_1["$meta"]["pagination"]["offset"] == 0
|
|
47
|
+
|
|
48
|
+
response = await api_client.get("/extensions?limit=5&offset=5")
|
|
49
|
+
assert response.status_code == 200
|
|
50
|
+
page_2 = response.json()
|
|
51
|
+
assert len(page_2["data"]) == 5
|
|
52
|
+
assert page_2["$meta"]["pagination"]["offset"] == 5
|
|
53
|
+
|
|
54
|
+
assert sorted(page_1["data"], key=lambda x: x["id"]) != sorted(
|
|
55
|
+
page_2["data"], key=lambda x: x["id"]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.asyncio
|
|
60
|
+
async def test_register_extension(mocker: MockerFixture, api_client: AsyncClient):
|
|
61
|
+
mocked_register = mocker.patch(
|
|
62
|
+
"mrok.controller.routes.extensions.register_extension",
|
|
63
|
+
return_value={
|
|
64
|
+
"id": "a1b2cd",
|
|
65
|
+
"name": "ext-1234-5678",
|
|
66
|
+
"tags": {
|
|
67
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
68
|
+
"account": "ACC-1234-5678",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
response = await api_client.post(
|
|
74
|
+
"/extensions",
|
|
75
|
+
json={
|
|
76
|
+
"extension": {"id": "EXT-1234-5678"},
|
|
77
|
+
"tags": {"account": "ACC-1234-5678"},
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
assert response.status_code == 201
|
|
81
|
+
assert response.json() == {
|
|
82
|
+
"id": "a1b2cd",
|
|
83
|
+
"extension": {"id": "EXT-1234-5678"},
|
|
84
|
+
"name": "ext-1234-5678",
|
|
85
|
+
"tags": {
|
|
86
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
87
|
+
"account": "ACC-1234-5678",
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
assert mocked_register.call_count == 1
|
|
91
|
+
assert isinstance(mocked_register.mock_calls[0].args[0], LazySettings)
|
|
92
|
+
assert isinstance(mocked_register.mock_calls[0].args[1], ZitiManagementAPI)
|
|
93
|
+
assert mocked_register.mock_calls[0].args[2] == "EXT-1234-5678"
|
|
94
|
+
assert mocked_register.mock_calls[0].args[3] == {"account": "ACC-1234-5678"}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@pytest.mark.asyncio
|
|
98
|
+
async def test_register_extension_already_exists(mocker: MockerFixture, api_client: AsyncClient):
|
|
99
|
+
mocker.patch(
|
|
100
|
+
"mrok.controller.routes.extensions.register_extension",
|
|
101
|
+
side_effect=ServiceAlreadyRegisteredError("Extension `EXT-1234-5678` already registered."),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
response = await api_client.post(
|
|
105
|
+
"/extensions",
|
|
106
|
+
json={
|
|
107
|
+
"extension": {"id": "EXT-1234-5678"},
|
|
108
|
+
"tags": {"account": "ACC-1234-5678"},
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
assert response.status_code == 400
|
|
112
|
+
assert response.json() == {"detail": "Extension `EXT-1234-5678` already registered."}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@pytest.mark.asyncio
|
|
116
|
+
@pytest.mark.parametrize("exc_type", [ProxyIdentityNotFoundError, ConfigTypeNotFoundError])
|
|
117
|
+
async def test_register_extension_mrok_not_configured(
|
|
118
|
+
mocker: MockerFixture,
|
|
119
|
+
api_client: AsyncClient,
|
|
120
|
+
exc_type: MrokError,
|
|
121
|
+
):
|
|
122
|
+
mocker.patch(
|
|
123
|
+
"mrok.controller.routes.extensions.register_extension",
|
|
124
|
+
side_effect=exc_type("this is the error."), # type: ignore
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
response = await api_client.post(
|
|
128
|
+
"/extensions",
|
|
129
|
+
json={
|
|
130
|
+
"extension": {"id": "EXT-1234-5678"},
|
|
131
|
+
"tags": {"account": "ACC-1234-5678"},
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
assert response.status_code == 400
|
|
135
|
+
assert response.json() == {"detail": "OpenZiti not configured properly: this is the error."}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@pytest.mark.asyncio
|
|
139
|
+
async def test_get_extension(
|
|
140
|
+
settings_factory: SettingsFactory,
|
|
141
|
+
api_client: AsyncClient,
|
|
142
|
+
httpx_mock: HTTPXMock,
|
|
143
|
+
):
|
|
144
|
+
settings = settings_factory()
|
|
145
|
+
query = quote(
|
|
146
|
+
f'(id="EXT-1234-5678" or name="ext-1234-5678") and tags.{MROK_VERSION_TAG_NAME} != null'
|
|
147
|
+
)
|
|
148
|
+
httpx_mock.add_response(
|
|
149
|
+
method="GET",
|
|
150
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/services?filter={query}",
|
|
151
|
+
json={
|
|
152
|
+
"meta": {"pagination": {"totalCount": 1}},
|
|
153
|
+
"data": [
|
|
154
|
+
{
|
|
155
|
+
"id": "svc1",
|
|
156
|
+
"name": "ext-1234-5678",
|
|
157
|
+
"tags": {MROK_VERSION_TAG_NAME: "0.0.0.dev0"},
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
response = await api_client.get("/extensions/EXT-1234-5678")
|
|
164
|
+
assert response.status_code == 200
|
|
165
|
+
assert response.json() == {
|
|
166
|
+
"id": "svc1",
|
|
167
|
+
"extension": {"id": "EXT-1234-5678"},
|
|
168
|
+
"name": "ext-1234-5678",
|
|
169
|
+
"tags": {
|
|
170
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@pytest.mark.asyncio
|
|
176
|
+
async def test_get_extension_not_found(
|
|
177
|
+
settings_factory: SettingsFactory,
|
|
178
|
+
api_client: AsyncClient,
|
|
179
|
+
httpx_mock: HTTPXMock,
|
|
180
|
+
):
|
|
181
|
+
settings = settings_factory()
|
|
182
|
+
query = quote(
|
|
183
|
+
f'(id="EXT-1234-5678" or name="ext-1234-5678") and tags.{MROK_VERSION_TAG_NAME} != null'
|
|
184
|
+
)
|
|
185
|
+
httpx_mock.add_response(
|
|
186
|
+
method="GET",
|
|
187
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/services?filter={query}",
|
|
188
|
+
json={
|
|
189
|
+
"meta": {"pagination": {"totalCount": 0}},
|
|
190
|
+
"data": [],
|
|
191
|
+
},
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
response = await api_client.get("/extensions/EXT-1234-5678")
|
|
195
|
+
assert response.status_code == 404
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@pytest.mark.asyncio
|
|
199
|
+
async def test_delete_extension(
|
|
200
|
+
mocker: MockerFixture,
|
|
201
|
+
api_client: AsyncClient,
|
|
202
|
+
):
|
|
203
|
+
mocker.patch(
|
|
204
|
+
"mrok.controller.routes.extensions.unregister_extension",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
response = await api_client.delete("/extensions/EXT-1234-5678")
|
|
208
|
+
assert response.status_code == 204
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@pytest.mark.asyncio
|
|
212
|
+
async def test_delete_extension_not_found(
|
|
213
|
+
mocker: MockerFixture,
|
|
214
|
+
api_client: AsyncClient,
|
|
215
|
+
):
|
|
216
|
+
mocker.patch(
|
|
217
|
+
"mrok.controller.routes.extensions.unregister_extension",
|
|
218
|
+
side_effect=ServiceNotFoundError("not found"),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
response = await api_client.delete("/extensions/EXT-1234-5678")
|
|
222
|
+
assert response.status_code == 404
|
|
223
|
+
|
|
224
|
+
|
|
13
225
|
@pytest.mark.asyncio
|
|
14
226
|
async def test_list_instances(
|
|
15
227
|
mocker: MockerFixture,
|
|
@@ -19,7 +231,8 @@ async def test_list_instances(
|
|
|
19
231
|
):
|
|
20
232
|
settings = settings_factory()
|
|
21
233
|
mocker.patch(
|
|
22
|
-
"mrok.controller.routes.fetch_extension_or_404",
|
|
234
|
+
"mrok.controller.routes.extensions.fetch_extension_or_404",
|
|
235
|
+
return_value={"name": "ext-1234-5678"},
|
|
23
236
|
)
|
|
24
237
|
query = quote(f'tags.{MROK_SERVICE_TAG_NAME} = "ext-1234-5678"')
|
|
25
238
|
httpx_mock.add_response(
|
|
@@ -58,10 +271,11 @@ async def test_list_instances(
|
|
|
58
271
|
@pytest.mark.asyncio
|
|
59
272
|
async def test_register_instance(mocker: MockerFixture, api_client: AsyncClient):
|
|
60
273
|
mocker.patch(
|
|
61
|
-
"mrok.controller.routes.fetch_extension_or_404",
|
|
274
|
+
"mrok.controller.routes.extensions.fetch_extension_or_404",
|
|
275
|
+
return_value={"name": "ext-1234-5678"},
|
|
62
276
|
)
|
|
63
277
|
mocked_register = mocker.patch(
|
|
64
|
-
"mrok.controller.routes.register_instance",
|
|
278
|
+
"mrok.controller.routes.extensions.register_instance",
|
|
65
279
|
return_value=(
|
|
66
280
|
{
|
|
67
281
|
"id": "a1b2cd",
|
|
@@ -114,7 +328,8 @@ async def test_get_instance(
|
|
|
114
328
|
httpx_mock: HTTPXMock,
|
|
115
329
|
):
|
|
116
330
|
mocker.patch(
|
|
117
|
-
"mrok.controller.routes.fetch_extension_or_404",
|
|
331
|
+
"mrok.controller.routes.extensions.fetch_extension_or_404",
|
|
332
|
+
return_value={"name": "ext-1234-5678"},
|
|
118
333
|
)
|
|
119
334
|
settings = settings_factory()
|
|
120
335
|
query = quote(
|
|
@@ -161,7 +376,8 @@ async def test_get_instance_by_instance_id(
|
|
|
161
376
|
httpx_mock: HTTPXMock,
|
|
162
377
|
):
|
|
163
378
|
mocker.patch(
|
|
164
|
-
"mrok.controller.routes.fetch_extension_or_404",
|
|
379
|
+
"mrok.controller.routes.extensions.fetch_extension_or_404",
|
|
380
|
+
return_value={"name": "ext-1234-5678"},
|
|
165
381
|
)
|
|
166
382
|
settings = settings_factory()
|
|
167
383
|
query = quote(f'(id="ins1" or name="ins1") and tags.{MROK_VERSION_TAG_NAME} != null')
|
|
@@ -205,7 +421,8 @@ async def test_get_instance_not_found(
|
|
|
205
421
|
httpx_mock: HTTPXMock,
|
|
206
422
|
):
|
|
207
423
|
mocker.patch(
|
|
208
|
-
"mrok.controller.routes.fetch_extension_or_404",
|
|
424
|
+
"mrok.controller.routes.extensions.fetch_extension_or_404",
|
|
425
|
+
return_value={"name": "ext-1234-5678"},
|
|
209
426
|
)
|
|
210
427
|
settings = settings_factory()
|
|
211
428
|
query = quote(
|
|
@@ -231,11 +448,11 @@ async def test_delete_instance(
|
|
|
231
448
|
api_client: AsyncClient,
|
|
232
449
|
):
|
|
233
450
|
mocker.patch(
|
|
234
|
-
"mrok.controller.routes.fetch_instance_or_404",
|
|
451
|
+
"mrok.controller.routes.extensions.fetch_instance_or_404",
|
|
235
452
|
return_value={"name": "ins-1234-5678.ext-1234-5678"},
|
|
236
453
|
)
|
|
237
454
|
mocker.patch(
|
|
238
|
-
"mrok.controller.routes.unregister_instance",
|
|
455
|
+
"mrok.controller.routes.extensions.unregister_instance",
|
|
239
456
|
)
|
|
240
457
|
|
|
241
458
|
response = await api_client.delete("/extensions/EXT-1234-5678/instances/INS-1234-5678-0001")
|
|
@@ -243,7 +460,7 @@ async def test_delete_instance(
|
|
|
243
460
|
|
|
244
461
|
|
|
245
462
|
@pytest.mark.asyncio
|
|
246
|
-
async def
|
|
463
|
+
async def test_delete_instance_extension_not_found(
|
|
247
464
|
mocker: MockerFixture,
|
|
248
465
|
api_client: AsyncClient,
|
|
249
466
|
settings_factory: SettingsFactory,
|
|
@@ -251,7 +468,8 @@ async def test_delete_extension_not_found(
|
|
|
251
468
|
):
|
|
252
469
|
settings = settings_factory()
|
|
253
470
|
mocker.patch(
|
|
254
|
-
"mrok.controller.routes.fetch_extension_or_404",
|
|
471
|
+
"mrok.controller.routes.extensions.fetch_extension_or_404",
|
|
472
|
+
return_value={"name": "ext-1234-5678"},
|
|
255
473
|
)
|
|
256
474
|
|
|
257
475
|
query = quote(
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from urllib.parse import quote
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from httpx import AsyncClient
|
|
5
|
+
from pytest_httpx import HTTPXMock
|
|
6
|
+
|
|
7
|
+
from mrok.ziti.constants import MROK_SERVICE_TAG_NAME, MROK_VERSION_TAG_NAME
|
|
8
|
+
from tests.conftest import SettingsFactory
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.asyncio
|
|
12
|
+
async def test_list_instances(
|
|
13
|
+
api_client: AsyncClient,
|
|
14
|
+
settings_factory: SettingsFactory,
|
|
15
|
+
httpx_mock: HTTPXMock,
|
|
16
|
+
):
|
|
17
|
+
settings = settings_factory()
|
|
18
|
+
httpx_mock.add_response(
|
|
19
|
+
method="GET",
|
|
20
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/identities?&limit=10&offset=0",
|
|
21
|
+
json={
|
|
22
|
+
"meta": {"pagination": {"totalCount": 15, "limit": 10, "offset": 0}},
|
|
23
|
+
"data": [{"id": f"ins{i}", "name": "ins.svc"} for i in range(10)],
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
httpx_mock.add_response(
|
|
27
|
+
method="GET",
|
|
28
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/identities?&limit=10&offset=10",
|
|
29
|
+
json={
|
|
30
|
+
"meta": {"pagination": {"totalCount": 15, "limit": 10, "offset": 10}},
|
|
31
|
+
"data": [{"id": f"ins{i}", "name": "ins.svc"} for i in range(11, 16)],
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
response = await api_client.get("/instances?limit=10&offset=0")
|
|
35
|
+
assert response.status_code == 200
|
|
36
|
+
page_1 = response.json()
|
|
37
|
+
assert len(page_1["data"]) == 10
|
|
38
|
+
assert page_1["$meta"]["pagination"]["offset"] == 0
|
|
39
|
+
|
|
40
|
+
response = await api_client.get("/instances?limit=10&offset=10")
|
|
41
|
+
assert response.status_code == 200
|
|
42
|
+
page_2 = response.json()
|
|
43
|
+
assert len(page_2["data"]) == 5
|
|
44
|
+
assert page_2["$meta"]["pagination"]["offset"] == 10
|
|
45
|
+
|
|
46
|
+
assert sorted(page_1["data"], key=lambda x: x["id"]) != sorted(
|
|
47
|
+
page_2["data"], key=lambda x: x["id"]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.mark.asyncio
|
|
52
|
+
async def test_get_instance(
|
|
53
|
+
settings_factory: SettingsFactory,
|
|
54
|
+
api_client: AsyncClient,
|
|
55
|
+
httpx_mock: HTTPXMock,
|
|
56
|
+
):
|
|
57
|
+
settings = settings_factory()
|
|
58
|
+
query = quote(
|
|
59
|
+
'(id="INS-1234-1234-0001.ext-1234-1234" or name="ins-1234-1234-0001.ext-1234-1234") '
|
|
60
|
+
f"and tags.{MROK_VERSION_TAG_NAME} != null"
|
|
61
|
+
)
|
|
62
|
+
httpx_mock.add_response(
|
|
63
|
+
method="GET",
|
|
64
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/identities?filter={query}",
|
|
65
|
+
json={
|
|
66
|
+
"meta": {"pagination": {"totalCount": 1}},
|
|
67
|
+
"data": [
|
|
68
|
+
{
|
|
69
|
+
"id": "ins1",
|
|
70
|
+
"name": "ins-1234-1234-0001.ext-1234-1234",
|
|
71
|
+
"tags": {
|
|
72
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
73
|
+
MROK_SERVICE_TAG_NAME: "ext-1234-1234",
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
response = await api_client.get("/instances/INS-1234-1234-0001.ext-1234-1234")
|
|
81
|
+
assert response.status_code == 200
|
|
82
|
+
assert response.json() == {
|
|
83
|
+
"id": "ins1",
|
|
84
|
+
"identity": None,
|
|
85
|
+
"extension": {"id": "EXT-1234-1234"},
|
|
86
|
+
"instance": {"id": "INS-1234-1234-0001"},
|
|
87
|
+
"name": "ins-1234-1234-0001.ext-1234-1234",
|
|
88
|
+
"tags": {
|
|
89
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
90
|
+
MROK_SERVICE_TAG_NAME: "ext-1234-1234",
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.mark.asyncio
|
|
96
|
+
async def test_get_instance_by_instance_id(
|
|
97
|
+
settings_factory: SettingsFactory,
|
|
98
|
+
api_client: AsyncClient,
|
|
99
|
+
httpx_mock: HTTPXMock,
|
|
100
|
+
):
|
|
101
|
+
settings = settings_factory()
|
|
102
|
+
query = quote(f'(id="ins1" or name="ins1") and tags.{MROK_VERSION_TAG_NAME} != null')
|
|
103
|
+
httpx_mock.add_response(
|
|
104
|
+
method="GET",
|
|
105
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/identities?filter={query}",
|
|
106
|
+
json={
|
|
107
|
+
"meta": {"pagination": {"totalCount": 1}},
|
|
108
|
+
"data": [
|
|
109
|
+
{
|
|
110
|
+
"id": "ins1",
|
|
111
|
+
"name": "ins-1234-1234-0001.ext-1234-1234",
|
|
112
|
+
"tags": {
|
|
113
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
114
|
+
MROK_SERVICE_TAG_NAME: "ext-1234-1234",
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
response = await api_client.get("/instances/ins1")
|
|
122
|
+
assert response.status_code == 200
|
|
123
|
+
assert response.json() == {
|
|
124
|
+
"id": "ins1",
|
|
125
|
+
"extension": {"id": "EXT-1234-1234"},
|
|
126
|
+
"instance": {"id": "INS-1234-1234-0001"},
|
|
127
|
+
"name": "ins-1234-1234-0001.ext-1234-1234",
|
|
128
|
+
"tags": {
|
|
129
|
+
MROK_VERSION_TAG_NAME: "0.0.0.dev0",
|
|
130
|
+
MROK_SERVICE_TAG_NAME: "ext-1234-1234",
|
|
131
|
+
},
|
|
132
|
+
"identity": None,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.asyncio
|
|
137
|
+
async def test_get_instance_not_found(
|
|
138
|
+
settings_factory: SettingsFactory,
|
|
139
|
+
api_client: AsyncClient,
|
|
140
|
+
httpx_mock: HTTPXMock,
|
|
141
|
+
):
|
|
142
|
+
settings = settings_factory()
|
|
143
|
+
query = quote(
|
|
144
|
+
'(id="INS-1234-1234-0001.ext-1234-1234" or name="ins-1234-1234-0001.ext-1234-1234") '
|
|
145
|
+
f"and tags.{MROK_VERSION_TAG_NAME} != null"
|
|
146
|
+
)
|
|
147
|
+
httpx_mock.add_response(
|
|
148
|
+
method="GET",
|
|
149
|
+
url=f"{settings.ziti.api.management}/edge/management/v1/identities?filter={query}",
|
|
150
|
+
json={
|
|
151
|
+
"meta": {"pagination": {"totalCount": 10}},
|
|
152
|
+
"data": [],
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
response = await api_client.get("/instances/INS-1234-1234-0001.ext-1234-1234")
|
|
157
|
+
assert response.status_code == 404
|
|
File without changes
|
|
@@ -236,6 +236,7 @@ async def test_search_methods(
|
|
|
236
236
|
("method_name", "endpoint"),
|
|
237
237
|
[
|
|
238
238
|
("delete_config", "/configs"),
|
|
239
|
+
("delete_config_type", "/config-types"),
|
|
239
240
|
("delete_service_policy", "/service-policies"),
|
|
240
241
|
("delete_service_router_policy", "/service-edge-router-policies"),
|
|
241
242
|
("delete_router_policy", "/edge-router-policies"),
|