sovereign 1.0.0a4__py3-none-any.whl → 1.0.0b148__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.
Potentially problematic release.
This version of sovereign might be problematic. Click here for more details.
- sovereign/__init__.py +2 -2
- sovereign/app.py +3 -6
- sovereign/cache/__init__.py +12 -85
- sovereign/cache/backends/__init__.py +1 -1
- sovereign/cache/backends/s3.py +6 -24
- sovereign/cache/filesystem.py +5 -6
- sovereign/cache/types.py +0 -2
- sovereign/configuration.py +8 -42
- sovereign/context.py +9 -8
- sovereign/dynamic_config/__init__.py +4 -3
- sovereign/dynamic_config/deser.py +1 -1
- sovereign/dynamic_config/loaders.py +3 -3
- sovereign/error_info.py +3 -2
- sovereign/events.py +3 -3
- sovereign/logging/access_logger.py +1 -1
- sovereign/logging/application_logger.py +1 -1
- sovereign/logging/bootstrapper.py +1 -1
- sovereign/modifiers/lib.py +1 -1
- sovereign/rendering.py +90 -22
- sovereign/response_class.py +2 -2
- sovereign/server.py +26 -45
- sovereign/sources/__init__.py +3 -0
- sovereign/sources/file.py +21 -0
- sovereign/sources/inline.py +39 -0
- sovereign/sources/lib.py +41 -0
- sovereign/sources/poller.py +537 -0
- sovereign/statistics.py +1 -2
- sovereign/testing/loaders.py +0 -1
- sovereign/tracing.py +5 -6
- sovereign/types.py +10 -15
- sovereign/utils/auth.py +2 -3
- sovereign/utils/crypto/suites/disabled_cipher.py +2 -2
- sovereign/utils/dictupdate.py +1 -1
- sovereign/utils/eds.py +1 -3
- sovereign/utils/entry_point_loader.py +2 -2
- sovereign/utils/mock.py +3 -4
- sovereign/utils/resources.py +1 -1
- sovereign/utils/templates.py +2 -4
- sovereign/utils/timer.py +3 -5
- sovereign/utils/weighted_clusters.py +1 -2
- sovereign/views/__init__.py +3 -6
- sovereign/views/api.py +7 -28
- sovereign/views/crypto.py +1 -1
- sovereign/views/discovery.py +5 -20
- sovereign/views/healthchecks.py +27 -45
- sovereign/views/interface.py +10 -70
- sovereign/worker.py +31 -20
- {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/METADATA +3 -4
- sovereign-1.0.0b148.dist-info/RECORD +77 -0
- {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/entry_points.txt +0 -8
- sovereign/rendering_common.py +0 -91
- sovereign/v2/__init__.py +0 -0
- sovereign/v2/data/data_store.py +0 -621
- sovereign/v2/data/render_discovery_response.py +0 -24
- sovereign/v2/data/repositories.py +0 -90
- sovereign/v2/data/utils.py +0 -33
- sovereign/v2/data/worker_queue.py +0 -273
- sovereign/v2/jobs/refresh_context.py +0 -117
- sovereign/v2/jobs/render_discovery_job.py +0 -145
- sovereign/v2/logging.py +0 -81
- sovereign/v2/types.py +0 -41
- sovereign/v2/web.py +0 -101
- sovereign/v2/worker.py +0 -199
- sovereign-1.0.0a4.dist-info/RECORD +0 -85
- {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/WHEEL +0 -0
sovereign/utils/eds.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import random
|
|
2
|
+
from typing import Dict, Any, Optional, List
|
|
2
3
|
from copy import deepcopy
|
|
3
|
-
from typing import Any, Dict, List, Optional
|
|
4
|
-
|
|
5
4
|
from starlette.exceptions import HTTPException
|
|
6
|
-
|
|
7
5
|
from sovereign.configuration import config
|
|
8
6
|
from sovereign.types import DiscoveryRequest
|
|
9
7
|
from sovereign.utils.templates import resolve
|
sovereign/utils/mock.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import ast
|
|
2
1
|
import re
|
|
2
|
+
import ast
|
|
3
|
+
from typing import Optional, Dict, List
|
|
3
4
|
from random import randint
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
from sovereign.types import DiscoveryRequest, Locality, Node, Status
|
|
5
|
+
from sovereign.types import DiscoveryRequest, Node, Locality, Status
|
|
7
6
|
|
|
8
7
|
scrub = re.compile(r"[^a-zA-Z_\.]")
|
|
9
8
|
|
sovereign/utils/resources.py
CHANGED
sovereign/utils/templates.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import List, Optional, Any, Dict
|
|
2
2
|
from socket import gethostbyname_ex
|
|
3
|
-
from
|
|
4
|
-
|
|
3
|
+
from socket import gaierror as dns_error
|
|
5
4
|
from starlette.exceptions import HTTPException
|
|
6
|
-
|
|
7
5
|
from sovereign import config, stats
|
|
8
6
|
|
|
9
7
|
REGIONS = config.legacy_fields.regions
|
sovereign/utils/timer.py
CHANGED
|
@@ -6,11 +6,9 @@ from typing import Any, Callable, Coroutine, NoReturn
|
|
|
6
6
|
from croniter import croniter
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def wait_until(
|
|
10
|
-
now = datetime.now(
|
|
11
|
-
|
|
12
|
-
target_dt = target_dt.replace(tzinfo=dt.timezone.utc)
|
|
13
|
-
sleep_time = (target_dt - now).total_seconds()
|
|
9
|
+
def wait_until(dt: datetime) -> float:
|
|
10
|
+
now = datetime.now()
|
|
11
|
+
sleep_time = (dt - now).total_seconds()
|
|
14
12
|
return sleep_time
|
|
15
13
|
|
|
16
14
|
|
sovereign/views/__init__.py
CHANGED
sovereign/views/api.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from fastapi import APIRouter,
|
|
4
|
+
from fastapi import APIRouter, Query, Path
|
|
5
5
|
from fastapi.responses import Response
|
|
6
6
|
|
|
7
|
-
from sovereign.cache import Entry
|
|
8
|
-
from sovereign.configuration import ConfiguredResourceTypes, config
|
|
9
|
-
from sovereign.utils.mock import mock_discovery_request
|
|
10
|
-
from sovereign.v2.web import wait_for_discovery_response
|
|
11
7
|
from sovereign.views import reader
|
|
8
|
+
from sovereign.configuration import ConfiguredResourceTypes
|
|
9
|
+
from sovereign.utils.mock import mock_discovery_request
|
|
12
10
|
|
|
13
11
|
router = APIRouter()
|
|
14
12
|
|
|
@@ -27,10 +25,9 @@ def expand_metadata_to_expr(m):
|
|
|
27
25
|
yield from _traverse(m, "", exprs)
|
|
28
26
|
|
|
29
27
|
|
|
30
|
-
# noinspection DuplicatedCode
|
|
31
28
|
@router.get("/resources/{resource_type}", summary="Get resources for a given type")
|
|
32
29
|
async def resource(
|
|
33
|
-
resource_type:
|
|
30
|
+
resource_type: ConfiguredResourceTypes = Path(title="xDS Resource type"),
|
|
34
31
|
resource_name: Optional[str] = Query(None, title="Resource name"),
|
|
35
32
|
api_version: Optional[str] = Query("v3", title="Envoy API version"),
|
|
36
33
|
service_cluster: Optional[str] = Query("*", title="Envoy Service cluster"),
|
|
@@ -38,8 +35,6 @@ async def resource(
|
|
|
38
35
|
version: Optional[str] = Query(None, title="Envoy Semantic Version"),
|
|
39
36
|
metadata: Optional[str] = Query(None, title="Envoy node metadata to filter by"),
|
|
40
37
|
) -> Response:
|
|
41
|
-
# todo: rewrite for worker v2
|
|
42
|
-
|
|
43
38
|
expressions = [f"cluster={service_cluster}"]
|
|
44
39
|
try:
|
|
45
40
|
data = {"metadata": json.loads(metadata or "{}")}
|
|
@@ -56,24 +51,8 @@ async def resource(
|
|
|
56
51
|
"expressions": expressions,
|
|
57
52
|
}
|
|
58
53
|
req = mock_discovery_request(**{k: v for k, v in kwargs.items() if v is not None}) # type: ignore
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if config.worker_v2_enabled:
|
|
63
|
-
# we're set up to use v2 of the worker
|
|
64
|
-
discovery_response = await wait_for_discovery_response(req)
|
|
65
|
-
if discovery_response is not None:
|
|
66
|
-
entry = Entry(
|
|
67
|
-
text=discovery_response.model_dump_json(indent=None),
|
|
68
|
-
len=len(discovery_response.resources),
|
|
69
|
-
version=discovery_response.version_info,
|
|
70
|
-
node=req.node,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
else:
|
|
74
|
-
entry = await reader.blocking_read(req) # ty: ignore[possibly-missing-attribute]
|
|
75
|
-
|
|
76
|
-
if content := getattr(entry, "text", None):
|
|
54
|
+
response = await reader.blocking_read(req)
|
|
55
|
+
if content := getattr(response, "text", None):
|
|
77
56
|
return Response(content, media_type="application/json")
|
|
78
57
|
else:
|
|
79
58
|
return Response(
|
sovereign/views/crypto.py
CHANGED
|
@@ -5,8 +5,8 @@ from fastapi.responses import JSONResponse
|
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
7
|
from sovereign import logs, server_cipher_container
|
|
8
|
-
from sovereign.configuration import EncryptionConfig
|
|
9
8
|
from sovereign.response_class import json_response_class
|
|
9
|
+
from sovereign.configuration import EncryptionConfig
|
|
10
10
|
from sovereign.utils.crypto.crypto import CipherContainer
|
|
11
11
|
from sovereign.utils.crypto.suites import EncryptionType
|
|
12
12
|
|
sovereign/views/discovery.py
CHANGED
|
@@ -2,15 +2,14 @@ from fastapi import Body, Header
|
|
|
2
2
|
from fastapi.responses import Response
|
|
3
3
|
from fastapi.routing import APIRouter
|
|
4
4
|
|
|
5
|
-
from sovereign import cache,
|
|
5
|
+
from sovereign import cache, logs
|
|
6
|
+
from sovereign.views import reader
|
|
6
7
|
from sovereign.cache.types import Entry
|
|
8
|
+
from sovereign.utils.auth import authenticate
|
|
7
9
|
from sovereign.types import (
|
|
8
10
|
DiscoveryRequest,
|
|
9
11
|
DiscoveryResponse,
|
|
10
12
|
)
|
|
11
|
-
from sovereign.utils.auth import authenticate
|
|
12
|
-
from sovereign.v2.web import wait_for_discovery_response
|
|
13
|
-
from sovereign.views import reader
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def response_headers(
|
|
@@ -72,21 +71,7 @@ async def discovery_response(
|
|
|
72
71
|
return Response(status_code=304, headers=headers)
|
|
73
72
|
return Response(entry.text, media_type="application/json", headers=headers)
|
|
74
73
|
|
|
75
|
-
if
|
|
76
|
-
|
|
77
|
-
response = await wait_for_discovery_response(xds_req)
|
|
78
|
-
if response is not None:
|
|
79
|
-
entry = Entry(
|
|
80
|
-
text=response.model_dump_json(indent=None),
|
|
81
|
-
len=len(response.resources),
|
|
82
|
-
version=response.version_info,
|
|
83
|
-
node=xds_req.node,
|
|
84
|
-
)
|
|
85
|
-
return handle_response(entry)
|
|
86
|
-
|
|
87
|
-
else:
|
|
88
|
-
entry: Entry | None
|
|
89
|
-
if entry := await reader.blocking_read(xds_req): # ty: ignore[possibly-missing-attribute]
|
|
90
|
-
return handle_response(entry)
|
|
74
|
+
if entry := await reader.blocking_read(xds_req):
|
|
75
|
+
return handle_response(entry)
|
|
91
76
|
|
|
92
77
|
return Response(content="Something went wrong", status_code=500)
|
sovereign/views/healthchecks.py
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from typing_extensions import Annotated, Literal
|
|
2
3
|
|
|
3
|
-
import pydantic
|
|
4
4
|
import requests
|
|
5
|
-
|
|
6
|
-
from fastapi
|
|
5
|
+
import pydantic
|
|
6
|
+
from fastapi import Request, Response, Query
|
|
7
7
|
from fastapi.routing import APIRouter
|
|
8
|
-
from
|
|
8
|
+
from fastapi.responses import JSONResponse, PlainTextResponse
|
|
9
9
|
|
|
10
10
|
from sovereign import __version__
|
|
11
|
-
from sovereign.configuration import XDS_TEMPLATES, config
|
|
12
|
-
from sovereign.response_class import json_response_class
|
|
13
|
-
from sovereign.utils.mock import mock_discovery_request
|
|
14
|
-
from sovereign.v2.web import wait_for_discovery_response
|
|
15
11
|
from sovereign.views import reader
|
|
12
|
+
from sovereign.configuration import XDS_TEMPLATES
|
|
13
|
+
from sovereign.utils.mock import mock_discovery_request
|
|
14
|
+
from sovereign.response_class import json_response_class
|
|
15
|
+
|
|
16
16
|
|
|
17
17
|
router = APIRouter()
|
|
18
18
|
|
|
@@ -80,45 +80,27 @@ async def deep_check(
|
|
|
80
80
|
) -> Response:
|
|
81
81
|
result = DeepCheckResult()
|
|
82
82
|
for template in list(XDS_TEMPLATES["default"].keys()):
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
result.worker = "
|
|
101
|
-
|
|
102
|
-
try:
|
|
103
|
-
_ = await reader.blocking_read(discovery_request) # ty: ignore[possibly-missing-attribute]
|
|
104
|
-
result.templates[template] = "OK"
|
|
105
|
-
except Exception as e:
|
|
106
|
-
result.templates[template] = ("FAIL", f"Failed {template}: {str(e)}")
|
|
107
|
-
|
|
108
|
-
if not config.worker_v2_enabled:
|
|
109
|
-
for attempt in range(worker_attempts):
|
|
110
|
-
try:
|
|
111
|
-
worker_health = requests.get("http://localhost:9080/health")
|
|
112
|
-
if worker_health.ok:
|
|
113
|
-
result.worker = "OK"
|
|
114
|
-
break
|
|
115
|
-
except Exception as e:
|
|
116
|
-
result.worker = ("FAIL", str(e))
|
|
117
|
-
await asyncio.sleep(attempt)
|
|
118
|
-
|
|
83
|
+
try:
|
|
84
|
+
req = mock_discovery_request(
|
|
85
|
+
"v3",
|
|
86
|
+
template,
|
|
87
|
+
expressions=[f"cluster={envoy_service_cluster}"],
|
|
88
|
+
)
|
|
89
|
+
_ = await reader.blocking_read(req)
|
|
90
|
+
result.templates[template] = "OK"
|
|
91
|
+
except Exception as e:
|
|
92
|
+
result.templates[template] = ("FAIL", f"Failed {template}: {str(e)}")
|
|
93
|
+
for attempt in range(worker_attempts):
|
|
94
|
+
try:
|
|
95
|
+
worker_health = requests.get("http://localhost:9080/health")
|
|
96
|
+
if worker_health.ok:
|
|
97
|
+
result.worker = "OK"
|
|
98
|
+
break
|
|
99
|
+
except Exception as e:
|
|
100
|
+
result.worker = ("FAIL", str(e))
|
|
101
|
+
await asyncio.sleep(attempt)
|
|
119
102
|
if "json" in request.headers.get("Accept", ""):
|
|
120
103
|
return result.json_response()
|
|
121
|
-
|
|
122
104
|
return result.response()
|
|
123
105
|
|
|
124
106
|
|
sovereign/views/interface.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import logging
|
|
3
2
|
from collections import defaultdict
|
|
4
3
|
from typing import Any, Dict, List
|
|
5
4
|
|
|
@@ -8,17 +7,13 @@ from fastapi.encoders import jsonable_encoder
|
|
|
8
7
|
from fastapi.requests import Request
|
|
9
8
|
from fastapi.responses import HTMLResponse, JSONResponse, Response
|
|
10
9
|
from starlette.templating import Jinja2Templates
|
|
11
|
-
from structlog.typing import FilteringBoundLogger
|
|
12
10
|
|
|
13
11
|
from sovereign import __version__
|
|
14
|
-
from sovereign.
|
|
15
|
-
from sovereign.configuration import
|
|
12
|
+
from sovereign.views import reader
|
|
13
|
+
from sovereign.configuration import ConfiguredResourceTypes, XDS_TEMPLATES
|
|
16
14
|
from sovereign.response_class import json_response_class
|
|
17
15
|
from sovereign.utils.mock import NodeExpressionError, mock_discovery_request
|
|
18
16
|
from sovereign.utils.resources import get_package_file
|
|
19
|
-
from sovereign.v2.logging import get_named_logger
|
|
20
|
-
from sovereign.v2.web import wait_for_discovery_response
|
|
21
|
-
from sovereign.views import reader
|
|
22
17
|
|
|
23
18
|
router = APIRouter()
|
|
24
19
|
|
|
@@ -56,7 +51,6 @@ async def ui_main(request: Request) -> HTMLResponse:
|
|
|
56
51
|
)
|
|
57
52
|
|
|
58
53
|
|
|
59
|
-
# noinspection DuplicatedCode
|
|
60
54
|
@router.get(
|
|
61
55
|
"/resources/{xds_type}", summary="List available resources for a given xDS type"
|
|
62
56
|
)
|
|
@@ -75,12 +69,8 @@ async def resources(
|
|
|
75
69
|
),
|
|
76
70
|
debug: int = Query(0, title="Show debug information on errors"),
|
|
77
71
|
) -> HTMLResponse:
|
|
78
|
-
logger: FilteringBoundLogger = get_named_logger(
|
|
79
|
-
f"{__name__}.{resources.__qualname__} ({__file__})",
|
|
80
|
-
level=logging.DEBUG,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
72
|
ret: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
73
|
+
response = None
|
|
84
74
|
try:
|
|
85
75
|
mock_request = mock_discovery_request(
|
|
86
76
|
api_version,
|
|
@@ -101,26 +91,9 @@ async def resources(
|
|
|
101
91
|
clear_cookie = True
|
|
102
92
|
error = str(e)
|
|
103
93
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if config.worker_v2_enabled:
|
|
109
|
-
# we're set up to use v2 of the worker
|
|
110
|
-
discovery_response = await wait_for_discovery_response(mock_request)
|
|
111
|
-
if discovery_response is not None:
|
|
112
|
-
entry = Entry(
|
|
113
|
-
text=discovery_response.model_dump_json(indent=None),
|
|
114
|
-
len=len(discovery_response.resources),
|
|
115
|
-
version=discovery_response.version_info,
|
|
116
|
-
node=mock_request.node,
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
else:
|
|
120
|
-
entry = await reader.blocking_read(mock_request) # ty: ignore[possibly-missing-attribute]
|
|
121
|
-
|
|
122
|
-
if entry:
|
|
123
|
-
ret["resources"] = json.loads(entry.text).get("resources", [])
|
|
94
|
+
response = await reader.blocking_read(mock_request)
|
|
95
|
+
if response:
|
|
96
|
+
ret["resources"] = json.loads(response.text).get("resources", [])
|
|
124
97
|
|
|
125
98
|
resp = html_templates.TemplateResponse(
|
|
126
99
|
request=request,
|
|
@@ -128,7 +101,7 @@ async def resources(
|
|
|
128
101
|
media_type="text/html",
|
|
129
102
|
context={
|
|
130
103
|
"show_debuginfo": True if debug else False,
|
|
131
|
-
"discovery_response":
|
|
104
|
+
"discovery_response": response,
|
|
132
105
|
"discovery_request": mock_request,
|
|
133
106
|
"resources": ret["resources"],
|
|
134
107
|
"resource_type": xds_type,
|
|
@@ -144,7 +117,6 @@ async def resources(
|
|
|
144
117
|
return resp
|
|
145
118
|
|
|
146
119
|
|
|
147
|
-
# noinspection DuplicatedCode
|
|
148
120
|
@router.get(
|
|
149
121
|
"/resources/{xds_type}/{resource_name}",
|
|
150
122
|
summary="Return JSON representation of a resource",
|
|
@@ -163,11 +135,6 @@ async def resource(
|
|
|
163
135
|
"__any__", title="The clients envoy version to emulate in this XDS request"
|
|
164
136
|
),
|
|
165
137
|
) -> Response:
|
|
166
|
-
logger: FilteringBoundLogger = get_named_logger(
|
|
167
|
-
f"{__name__}.{resources.__qualname__} ({__file__})",
|
|
168
|
-
level=logging.DEBUG,
|
|
169
|
-
)
|
|
170
|
-
|
|
171
138
|
mock_request = mock_discovery_request(
|
|
172
139
|
api_version,
|
|
173
140
|
xds_type,
|
|
@@ -175,27 +142,8 @@ async def resource(
|
|
|
175
142
|
region=region,
|
|
176
143
|
expressions=node_expression.split(),
|
|
177
144
|
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
entry: Entry | None = None
|
|
182
|
-
|
|
183
|
-
if config.worker_v2_enabled:
|
|
184
|
-
# we're set up to use v2 of the worker
|
|
185
|
-
discovery_response = await wait_for_discovery_response(mock_request)
|
|
186
|
-
if discovery_response is not None:
|
|
187
|
-
entry = Entry(
|
|
188
|
-
text=discovery_response.model_dump_json(indent=None),
|
|
189
|
-
len=len(discovery_response.resources),
|
|
190
|
-
version=discovery_response.version_info,
|
|
191
|
-
node=mock_request.node,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
else:
|
|
195
|
-
entry = await reader.blocking_read(mock_request) # ty: ignore[possibly-missing-attribute]
|
|
196
|
-
|
|
197
|
-
if entry:
|
|
198
|
-
for res in json.loads(entry.text).get("resources", []):
|
|
145
|
+
if response := await reader.blocking_read(mock_request):
|
|
146
|
+
for res in json.loads(response.text).get("resources", []):
|
|
199
147
|
if res.get("name", res.get("cluster_name")) == resource_name:
|
|
200
148
|
safe_response = jsonable_encoder(res)
|
|
201
149
|
try:
|
|
@@ -226,11 +174,6 @@ async def virtual_hosts(
|
|
|
226
174
|
"__any__", title="The clients envoy version to emulate in this XDS request"
|
|
227
175
|
),
|
|
228
176
|
) -> Response:
|
|
229
|
-
logger: FilteringBoundLogger = get_named_logger(
|
|
230
|
-
f"{__name__}.{virtual_hosts.__qualname__} ({__file__})",
|
|
231
|
-
level=logging.DEBUG,
|
|
232
|
-
)
|
|
233
|
-
|
|
234
177
|
mock_request = mock_discovery_request(
|
|
235
178
|
api_version,
|
|
236
179
|
"routes",
|
|
@@ -238,10 +181,7 @@ async def virtual_hosts(
|
|
|
238
181
|
region=region,
|
|
239
182
|
expressions=node_expression.split(),
|
|
240
183
|
)
|
|
241
|
-
|
|
242
|
-
logger.debug("Making mock request", mock_request=mock_request)
|
|
243
|
-
|
|
244
|
-
if response := await reader.blocking_read(mock_request): # ty: ignore[possibly-missing-attribute]
|
|
184
|
+
if response := await reader.blocking_read(mock_request):
|
|
245
185
|
route_configs = [
|
|
246
186
|
resource_
|
|
247
187
|
for resource_ in json.loads(response.text).get("resources", [])
|
sovereign/worker.py
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
from contextlib import asynccontextmanager
|
|
3
2
|
from typing import final
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
4
|
|
|
5
|
-
from fastapi import
|
|
5
|
+
from fastapi import FastAPI, Body
|
|
6
6
|
|
|
7
|
-
from sovereign import (
|
|
8
|
-
application_logger as log,
|
|
9
|
-
)
|
|
10
7
|
from sovereign import (
|
|
11
8
|
cache,
|
|
12
|
-
disabled_ciphersuite,
|
|
13
9
|
rendering,
|
|
14
10
|
server_cipher_container,
|
|
11
|
+
disabled_ciphersuite,
|
|
12
|
+
application_logger as log,
|
|
15
13
|
stats,
|
|
16
14
|
)
|
|
17
|
-
from sovereign.configuration import config
|
|
18
15
|
from sovereign.context import TemplateContext
|
|
19
|
-
from sovereign.
|
|
20
|
-
from sovereign.
|
|
16
|
+
from sovereign.sources import SourcePoller
|
|
17
|
+
from sovereign.configuration import config
|
|
18
|
+
from sovereign.types import RegisterClientRequest, DiscoveryRequest
|
|
19
|
+
from sovereign.events import bus, Topic
|
|
21
20
|
|
|
22
21
|
|
|
23
|
-
# noinspection PyUnusedLocal
|
|
24
22
|
def hidden_field(*args, **kwargs):
|
|
25
23
|
return "(value hidden)"
|
|
26
24
|
|
|
@@ -90,6 +88,16 @@ if config.sources is not None:
|
|
|
90
88
|
matching_enabled = False
|
|
91
89
|
node_key = None
|
|
92
90
|
source_key = None
|
|
91
|
+
poller = SourcePoller(
|
|
92
|
+
sources=config.sources,
|
|
93
|
+
matching_enabled=matching_enabled,
|
|
94
|
+
node_match_key=node_key,
|
|
95
|
+
source_match_key=source_key,
|
|
96
|
+
source_refresh_rate=config.source_config.refresh_rate,
|
|
97
|
+
logger=log,
|
|
98
|
+
stats=stats,
|
|
99
|
+
)
|
|
100
|
+
context_middleware.append(poller.add_to_context)
|
|
93
101
|
|
|
94
102
|
|
|
95
103
|
async def render_on_event(ctx):
|
|
@@ -123,19 +131,18 @@ async def render_on_event(ctx):
|
|
|
123
131
|
|
|
124
132
|
async def render_on_demand(ctx):
|
|
125
133
|
while True:
|
|
126
|
-
|
|
134
|
+
id, request = await ONDEMAND.get()
|
|
127
135
|
stats.increment("template.render_on_demand")
|
|
128
136
|
log.debug(
|
|
129
|
-
f"Received on-demand request to render templates for {
|
|
137
|
+
f"Received on-demand request to render templates for {id} ({request})"
|
|
130
138
|
)
|
|
131
139
|
job = rendering.RenderJob(
|
|
132
|
-
id=
|
|
140
|
+
id=id, request=request, context=ctx.get_context(request)
|
|
133
141
|
)
|
|
134
142
|
_ = job.submit()
|
|
135
|
-
await ONDEMAND.task_done(
|
|
143
|
+
await ONDEMAND.task_done(id)
|
|
136
144
|
|
|
137
145
|
|
|
138
|
-
# noinspection PyProtectedMember
|
|
139
146
|
async def monitor_render_queue():
|
|
140
147
|
"""Periodically report render queue size metrics"""
|
|
141
148
|
while True:
|
|
@@ -158,6 +165,13 @@ async def lifespan(_: FastAPI):
|
|
|
158
165
|
event = await subscription.get()
|
|
159
166
|
log.debug(event.message)
|
|
160
167
|
|
|
168
|
+
# Source polling
|
|
169
|
+
if poller is not None:
|
|
170
|
+
log.debug("Starting source poller")
|
|
171
|
+
poller.lazy_load_modifiers(config.modifiers)
|
|
172
|
+
poller.lazy_load_global_modifiers(config.global_modifiers)
|
|
173
|
+
asyncio.create_task(poller.poll_forever())
|
|
174
|
+
|
|
161
175
|
log.debug("Worker lifespan initialized")
|
|
162
176
|
yield
|
|
163
177
|
|
|
@@ -165,10 +179,7 @@ async def lifespan(_: FastAPI):
|
|
|
165
179
|
worker = FastAPI(lifespan=lifespan)
|
|
166
180
|
if dsn := config.sentry_dsn.get_secret_value():
|
|
167
181
|
try:
|
|
168
|
-
# noinspection PyUnusedImports
|
|
169
182
|
import sentry_sdk
|
|
170
|
-
|
|
171
|
-
# noinspection PyUnusedImports
|
|
172
183
|
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
|
|
173
184
|
|
|
174
185
|
sentry_sdk.init(dsn)
|
|
@@ -188,6 +199,6 @@ async def client_add(
|
|
|
188
199
|
):
|
|
189
200
|
log.info(f"Received registration: {registration.request}")
|
|
190
201
|
xds = registration.request
|
|
191
|
-
|
|
192
|
-
ONDEMAND.put_nowait((
|
|
202
|
+
id, req = writer.register(xds)
|
|
203
|
+
ONDEMAND.put_nowait((id, req))
|
|
193
204
|
return "Registered", 200
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sovereign
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0b148
|
|
4
4
|
Summary: Envoy Proxy control-plane written in Python
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/sovereign/
|
|
6
6
|
Project-URL: Repository, https://bitbucket.org/atlassian/sovereign/src/master/
|
|
@@ -27,7 +27,7 @@ Requires-Dist: cachelib<0.11,>=0.10.2
|
|
|
27
27
|
Requires-Dist: cachetools<6,>=5.3.2
|
|
28
28
|
Requires-Dist: croniter<2,>=1.4.1
|
|
29
29
|
Requires-Dist: cryptography>=45.0.2
|
|
30
|
-
Requires-Dist: fastapi<0.
|
|
30
|
+
Requires-Dist: fastapi<0.117,>=0.116.0
|
|
31
31
|
Requires-Dist: glom<24,>=23.3.0
|
|
32
32
|
Requires-Dist: h11<0.17,>=0.16.0
|
|
33
33
|
Requires-Dist: jinja2<4,>=3.1.2
|
|
@@ -36,9 +36,8 @@ Requires-Dist: pydantic-settings<2.6.0
|
|
|
36
36
|
Requires-Dist: pydantic<3,>=2.7.2
|
|
37
37
|
Requires-Dist: pyyaml<7,>=6.0.1
|
|
38
38
|
Requires-Dist: requests<3,>=2.32.4
|
|
39
|
-
Requires-Dist: rich>=14.2.0
|
|
40
39
|
Requires-Dist: starlette-context<0.4,>=0.3.6
|
|
41
|
-
Requires-Dist: starlette<0.
|
|
40
|
+
Requires-Dist: starlette<0.48,>=0.47.2
|
|
42
41
|
Requires-Dist: structlog<24,>=23.1.0
|
|
43
42
|
Requires-Dist: supervisor<5,>=4.2.5
|
|
44
43
|
Requires-Dist: uvicorn<0.24,>=0.23.2
|