sovereign 0.26.0__tar.gz → 0.28.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.
Potentially problematic release.
This version of sovereign might be problematic. Click here for more details.
- {sovereign-0.26.0 → sovereign-0.28.0}/PKG-INFO +4 -2
- {sovereign-0.26.0 → sovereign-0.28.0}/pyproject.toml +10 -2
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/__init__.py +1 -1
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/configuration.py +1 -1
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/discovery.py +2 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/schemas.py +196 -188
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/templates/resources.html +16 -6
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/crypto.py +2 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/views/admin.py +2 -2
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/views/interface.py +2 -2
- {sovereign-0.26.0 → sovereign-0.28.0}/LICENSE.txt +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/README.md +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/app.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/config_loader.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/constants.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/context.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/error_info.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/logging/access_logger.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/logging/application_logger.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/logging/base_logger.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/logging/bootstrapper.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/logging/types.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/middlewares.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/modifiers/__init__.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/modifiers/lib.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/response_class.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/server.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/sources/__init__.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/sources/file.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/sources/inline.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/sources/lib.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/sources/poller.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/static/sass/style.scss +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/static/style.css +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/statistics.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/templates/base.html +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/templates/err.html +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/templates/ul_filter.html +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/testing/loaders.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/testing/modifiers.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/__init__.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/auth.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/__init__.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/suites/__init__.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/suites/aes_gcm_cipher.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/suites/base_cipher.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/suites/disabled_cipher.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/crypto/suites/fernet_cipher.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/dictupdate.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/eds.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/entry_point_loader.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/mock.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/resources.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/templates.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/timer.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/version_info.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/utils/weighted_clusters.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/views/__init__.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/views/crypto.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/views/discovery.py +0 -0
- {sovereign-0.26.0 → sovereign-0.28.0}/src/sovereign/views/healthchecks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sovereign
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.28.0
|
|
4
4
|
Summary: Envoy Proxy control-plane written in Python
|
|
5
5
|
Home-page: https://pypi.org/project/sovereign/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -44,13 +44,15 @@ Requires-Dist: glom (>=23.3.0,<24.0.0)
|
|
|
44
44
|
Requires-Dist: gunicorn (>=22.0.0,<23.0.0)
|
|
45
45
|
Requires-Dist: httptools (>=0.6.0,<0.7.0) ; extra == "httptools"
|
|
46
46
|
Requires-Dist: orjson (>=3.9.15,<4.0.0) ; extra == "orjson"
|
|
47
|
+
Requires-Dist: pydantic (>=2.7.2,<3.0.0)
|
|
48
|
+
Requires-Dist: pydantic-settings (>=2.3.1,<3.0.0)
|
|
47
49
|
Requires-Dist: redis (<=5.0.0)
|
|
48
50
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
49
51
|
Requires-Dist: sentry-sdk (>=1.23.1,<2.0.0) ; extra == "sentry"
|
|
50
52
|
Requires-Dist: structlog (>=23.1.0,<24.0.0)
|
|
51
53
|
Requires-Dist: ujson (>=5.8.0,<6.0.0) ; extra == "ujson"
|
|
52
54
|
Requires-Dist: uvicorn (>=0.23.2,<0.24.0)
|
|
53
|
-
Requires-Dist: uvloop (>=0.
|
|
55
|
+
Requires-Dist: uvloop (>=0.19.0,<0.20.0)
|
|
54
56
|
Project-URL: Documentation, https://vsyrakis.bitbucket.io/sovereign/docs/
|
|
55
57
|
Project-URL: Repository, https://bitbucket.org/atlassian/sovereign/src/master/
|
|
56
58
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "sovereign"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.28.0"
|
|
4
4
|
description = "Envoy Proxy control-plane written in Python"
|
|
5
5
|
license = "Apache-2.0"
|
|
6
6
|
packages = [
|
|
@@ -55,7 +55,7 @@ cachelib = "^0.10.2"
|
|
|
55
55
|
glom = "^23.3.0"
|
|
56
56
|
cryptography = "^42.0.5"
|
|
57
57
|
fastapi = "^0.110.0"
|
|
58
|
-
uvloop = "^0.
|
|
58
|
+
uvloop = "^0.19.0"
|
|
59
59
|
sentry-sdk = "^1.23.1"
|
|
60
60
|
boto3 = {version = "^1.28.62", optional = true}
|
|
61
61
|
datadog = {version = "^0.47.0", optional = true}
|
|
@@ -66,6 +66,8 @@ cashews = {extras = ["redis"], version = "^6.3.0", optional = true}
|
|
|
66
66
|
redis = {version = "<= 5.0.0", optional = true}
|
|
67
67
|
httptools = {version = "^0.6.0", optional = true}
|
|
68
68
|
cachetools = "^5.3.2"
|
|
69
|
+
pydantic = "^2.7.2"
|
|
70
|
+
pydantic-settings = "^2.3.1"
|
|
69
71
|
|
|
70
72
|
[tool.poetry.extras]
|
|
71
73
|
sentry = ["sentry-sdk"]
|
|
@@ -114,6 +116,12 @@ lint = { cmd = "pylint src/sovereign", help = "Run linter checks" }
|
|
|
114
116
|
[tool.black]
|
|
115
117
|
target-version = ['py311']
|
|
116
118
|
|
|
119
|
+
[tool.mypy]
|
|
120
|
+
plugins = [
|
|
121
|
+
"pydantic.mypy"
|
|
122
|
+
]
|
|
123
|
+
ignore_missing_imports = true
|
|
124
|
+
|
|
117
125
|
[tool.coverage.run]
|
|
118
126
|
omit = ["test/*"]
|
|
119
127
|
|
|
@@ -4,7 +4,7 @@ from importlib.metadata import version
|
|
|
4
4
|
from typing import Any, Mapping, Type
|
|
5
5
|
|
|
6
6
|
from fastapi.responses import JSONResponse
|
|
7
|
-
from pydantic
|
|
7
|
+
from pydantic import ValidationError
|
|
8
8
|
from starlette.templating import Jinja2Templates
|
|
9
9
|
|
|
10
10
|
from sovereign import config_loader
|
|
@@ -163,6 +163,8 @@ def filter_resources(
|
|
|
163
163
|
that does not match the name of the resource.
|
|
164
164
|
If Envoy did not specifically request anything, every resource is retained.
|
|
165
165
|
"""
|
|
166
|
+
if len(requested) == 0:
|
|
167
|
+
return generated
|
|
166
168
|
return [resource for resource in generated if resource_name(resource) in requested]
|
|
167
169
|
|
|
168
170
|
|
|
@@ -5,19 +5,20 @@ from dataclasses import dataclass
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from os import getenv
|
|
7
7
|
from types import ModuleType
|
|
8
|
-
from typing import Any, Dict, List, Optional, Tuple, Type, Union
|
|
8
|
+
from typing import Any, Dict, List, Optional, Self, Tuple, Type, Union
|
|
9
9
|
|
|
10
10
|
from croniter import CroniterBadCronError, croniter
|
|
11
11
|
from fastapi.responses import JSONResponse
|
|
12
12
|
from jinja2 import Template, meta
|
|
13
13
|
from pydantic import (
|
|
14
14
|
BaseModel,
|
|
15
|
-
|
|
15
|
+
ConfigDict,
|
|
16
16
|
Field,
|
|
17
17
|
SecretStr,
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
model_validator,
|
|
19
|
+
field_validator,
|
|
20
20
|
)
|
|
21
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
21
22
|
|
|
22
23
|
from sovereign.config_loader import Loadable, Serialization, jinja_env
|
|
23
24
|
from sovereign.utils.crypto.suites import EncryptionType
|
|
@@ -65,11 +66,13 @@ class StatsdConfig(BaseModel):
|
|
|
65
66
|
enabled: bool = False
|
|
66
67
|
use_ms: bool = True
|
|
67
68
|
|
|
68
|
-
@
|
|
69
|
+
@field_validator("host", mode="before")
|
|
70
|
+
@classmethod
|
|
69
71
|
def load_host(cls, v: str) -> Any:
|
|
70
72
|
return Loadable.from_legacy_fmt(v).load()
|
|
71
73
|
|
|
72
|
-
@
|
|
74
|
+
@field_validator("port", mode="before")
|
|
75
|
+
@classmethod
|
|
73
76
|
def load_port(cls, v: Union[int, str]) -> Any:
|
|
74
77
|
if isinstance(v, int):
|
|
75
78
|
return v
|
|
@@ -78,7 +81,8 @@ class StatsdConfig(BaseModel):
|
|
|
78
81
|
else:
|
|
79
82
|
raise ValueError(f"Received an invalid port: {v}")
|
|
80
83
|
|
|
81
|
-
@
|
|
84
|
+
@field_validator("tags", mode="before")
|
|
85
|
+
@classmethod
|
|
82
86
|
def load_tags(cls, v: Dict[str, Union[Loadable, str]]) -> Dict[str, Any]:
|
|
83
87
|
ret = dict()
|
|
84
88
|
for key, value in v.items():
|
|
@@ -108,14 +112,14 @@ class DiscoveryCacheConfig(BaseModel):
|
|
|
108
112
|
socket_keepalive: bool = True # Try to keep connections to redis around.
|
|
109
113
|
ttl: int = 60
|
|
110
114
|
|
|
111
|
-
@
|
|
112
|
-
def set_default_protocol(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return values
|
|
115
|
+
@model_validator(mode="after")
|
|
116
|
+
def set_default_protocol(self) -> Self:
|
|
117
|
+
if self.secure:
|
|
118
|
+
self.protocol = "rediss://"
|
|
119
|
+
return self
|
|
117
120
|
|
|
118
|
-
@
|
|
121
|
+
@model_validator(mode="before")
|
|
122
|
+
@classmethod
|
|
119
123
|
def set_environmental_variables(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
|
120
124
|
if host := getenv("SOVEREIGN_DISCOVERY_CACHE_REDIS_HOST"):
|
|
121
125
|
values["host"] = host
|
|
@@ -221,9 +225,9 @@ class ProcessedTemplate:
|
|
|
221
225
|
|
|
222
226
|
|
|
223
227
|
class Locality(BaseModel):
|
|
224
|
-
region: str = Field(None)
|
|
225
|
-
zone: str = Field(None)
|
|
226
|
-
sub_zone: str = Field(None)
|
|
228
|
+
region: Optional[str] = Field(None)
|
|
229
|
+
zone: Optional[str] = Field(None)
|
|
230
|
+
sub_zone: Optional[str] = Field(None)
|
|
227
231
|
|
|
228
232
|
|
|
229
233
|
class SemanticVersion(BaseModel):
|
|
@@ -255,8 +259,8 @@ class Node(BaseModel):
|
|
|
255
259
|
description="The ``--service-cluster`` configured by the Envoy client",
|
|
256
260
|
)
|
|
257
261
|
metadata: Dict[str, Any] = Field(default_factory=dict, title="Key:value metadata")
|
|
258
|
-
locality: Locality = Field(Locality(), title="Locality")
|
|
259
|
-
build_version: str = Field(
|
|
262
|
+
locality: Locality = Field(Locality(), title="Locality")
|
|
263
|
+
build_version: Optional[str] = Field(
|
|
260
264
|
None, # Optional in the v3 Envoy API
|
|
261
265
|
title="Envoy build/release version string",
|
|
262
266
|
description="Used to identify what version of Envoy the "
|
|
@@ -269,7 +273,7 @@ class Node(BaseModel):
|
|
|
269
273
|
client_features: List[str] = []
|
|
270
274
|
|
|
271
275
|
@property
|
|
272
|
-
def common(self) -> Tuple[str, str, str, BuildVersion, Locality]:
|
|
276
|
+
def common(self) -> Tuple[str, Optional[str], str, BuildVersion, Locality]:
|
|
273
277
|
"""
|
|
274
278
|
Returns fields that are the same in adjacent proxies
|
|
275
279
|
ie. proxies that are part of the same logical group
|
|
@@ -290,9 +294,11 @@ class Resources(List[str]):
|
|
|
290
294
|
"""
|
|
291
295
|
|
|
292
296
|
def __contains__(self, item: object) -> bool:
|
|
293
|
-
if
|
|
297
|
+
if (
|
|
298
|
+
len(self) == 0
|
|
299
|
+
): # TODO: refactor to remove overriding __contains__; its being used in multiple places
|
|
294
300
|
return True
|
|
295
|
-
return
|
|
301
|
+
return super().__contains__(item)
|
|
296
302
|
|
|
297
303
|
|
|
298
304
|
class Status(BaseModel):
|
|
@@ -301,24 +307,29 @@ class Status(BaseModel):
|
|
|
301
307
|
details: List[Any]
|
|
302
308
|
|
|
303
309
|
|
|
310
|
+
def resources_factory() -> Resources:
|
|
311
|
+
return Resources()
|
|
312
|
+
|
|
313
|
+
|
|
304
314
|
class DiscoveryRequest(BaseModel):
|
|
305
315
|
node: Node = Field(..., title="Node information about the envoy proxy")
|
|
306
316
|
version_info: str = Field(
|
|
307
317
|
"0", title="The version of the envoy clients current configuration"
|
|
308
318
|
)
|
|
309
319
|
resource_names: Resources = Field(
|
|
310
|
-
|
|
320
|
+
default_factory=resources_factory, title="List of requested resource names"
|
|
311
321
|
)
|
|
312
322
|
hide_private_keys: bool = False
|
|
313
323
|
type_url: Optional[str] = Field(
|
|
314
324
|
None, title="The corresponding type_url for the requested resource"
|
|
315
325
|
)
|
|
316
|
-
desired_controlplane: str = Field(
|
|
326
|
+
desired_controlplane: Optional[str] = Field(
|
|
317
327
|
None, title="The host header provided in the Discovery Request"
|
|
318
328
|
)
|
|
319
|
-
error_detail: Status = Field(
|
|
329
|
+
error_detail: Optional[Status] = Field(
|
|
320
330
|
None, title="Error details from the previous xDS request"
|
|
321
331
|
)
|
|
332
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
322
333
|
|
|
323
334
|
@property
|
|
324
335
|
def envoy_version(self) -> str:
|
|
@@ -328,6 +339,8 @@ class DiscoveryRequest(BaseModel):
|
|
|
328
339
|
except AssertionError:
|
|
329
340
|
try:
|
|
330
341
|
build_version = self.node.build_version
|
|
342
|
+
if build_version is None:
|
|
343
|
+
return "default"
|
|
331
344
|
_, version, *_ = build_version.split("/")
|
|
332
345
|
except (AttributeError, ValueError):
|
|
333
346
|
# TODO: log/metric this?
|
|
@@ -346,6 +359,11 @@ class DiscoveryRequest(BaseModel):
|
|
|
346
359
|
self.desired_controlplane,
|
|
347
360
|
)
|
|
348
361
|
|
|
362
|
+
@field_validator("resource_names", mode="before")
|
|
363
|
+
@classmethod
|
|
364
|
+
def validate_resources(cls, v: Union[Resources, List[str]]) -> Resources:
|
|
365
|
+
return Resources(v)
|
|
366
|
+
|
|
349
367
|
|
|
350
368
|
class DiscoveryResponse(BaseModel):
|
|
351
369
|
version_info: str = Field(
|
|
@@ -355,33 +373,35 @@ class DiscoveryResponse(BaseModel):
|
|
|
355
373
|
|
|
356
374
|
|
|
357
375
|
class SovereignAsgiConfig(BaseSettings):
|
|
358
|
-
host: str = "0.0.0.0"
|
|
359
|
-
port: int = 8080
|
|
360
|
-
keepalive: int = 5
|
|
361
|
-
workers: int =
|
|
362
|
-
|
|
376
|
+
host: str = Field("0.0.0.0", alias="SOVEREIGN_HOST")
|
|
377
|
+
port: int = Field(8080, alias="SOVEREIGN_PORT")
|
|
378
|
+
keepalive: int = Field(5, alias="SOVEREIGN_KEEPALIVE")
|
|
379
|
+
workers: int = Field(
|
|
380
|
+
default_factory=lambda: multiprocessing.cpu_count() * 2 + 1,
|
|
381
|
+
alias="SOVEREIGN_WORKERS",
|
|
382
|
+
)
|
|
383
|
+
threads: int = Field(1, alias="SOVEREIGN_THREADS")
|
|
363
384
|
reuse_port: bool = True
|
|
364
|
-
preload_app: bool = True
|
|
385
|
+
preload_app: bool = Field(True, alias="SOVEREIGN_PRELOAD")
|
|
365
386
|
log_level: str = "warning"
|
|
366
387
|
worker_class: str = "uvicorn.workers.UvicornWorker"
|
|
367
|
-
worker_timeout: int = 30
|
|
388
|
+
worker_timeout: int = Field(30, alias="SOVEREIGN_WORKER_TIMEOUT")
|
|
368
389
|
worker_tmp_dir: str = "/dev/shm"
|
|
369
|
-
graceful_timeout: int =
|
|
370
|
-
max_requests: int = 0
|
|
371
|
-
max_requests_jitter: int = 0
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
390
|
+
graceful_timeout: Optional[int] = Field(None)
|
|
391
|
+
max_requests: int = Field(0, alias="SOVEREIGN_MAX_REQUESTS")
|
|
392
|
+
max_requests_jitter: int = Field(0, alias="SOVEREIGN_MAX_REQUESTS_JITTER")
|
|
393
|
+
model_config = SettingsConfigDict(
|
|
394
|
+
env_file=".env",
|
|
395
|
+
extra="ignore",
|
|
396
|
+
env_file_encoding="utf-8",
|
|
397
|
+
populate_by_name=True,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
@model_validator(mode="after")
|
|
401
|
+
def validate_graceful_timeout(self) -> Self:
|
|
402
|
+
if self.graceful_timeout is None:
|
|
403
|
+
self.graceful_timeout = self.worker_timeout * 2
|
|
404
|
+
return self
|
|
385
405
|
|
|
386
406
|
def as_gunicorn_conf(self) -> Dict[str, Any]:
|
|
387
407
|
return {
|
|
@@ -405,54 +425,45 @@ class SovereignConfig(BaseSettings):
|
|
|
405
425
|
sources: List[ConfiguredSource]
|
|
406
426
|
templates: Dict[str, Dict[str, Union[str, Loadable]]]
|
|
407
427
|
template_context: Dict[str, Any] = {}
|
|
408
|
-
eds_priority_matrix: Dict[str, Dict[str,
|
|
428
|
+
eds_priority_matrix: Dict[str, Dict[str, int]] = {}
|
|
409
429
|
modifiers: List[str] = []
|
|
410
430
|
global_modifiers: List[str] = []
|
|
411
431
|
regions: List[str] = []
|
|
412
432
|
statsd: StatsdConfig = StatsdConfig()
|
|
413
|
-
auth_enabled: bool = False
|
|
414
|
-
auth_passwords: str = ""
|
|
415
|
-
encryption_key: str = ""
|
|
416
|
-
environment: str = "local"
|
|
417
|
-
debug_enabled: bool = False
|
|
418
|
-
sentry_dsn: str = ""
|
|
419
|
-
node_match_key: str = "cluster"
|
|
420
|
-
node_matching: bool = True
|
|
421
|
-
source_match_key: str =
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
433
|
+
auth_enabled: bool = Field(False, alias="SOVEREIGN_AUTH_ENABLED")
|
|
434
|
+
auth_passwords: str = Field("", alias="SOVEREIGN_AUTH_PASSWORDS")
|
|
435
|
+
encryption_key: str = Field("", alias="SOVEREIGN_ENCRYPTION_KEY")
|
|
436
|
+
environment: str = Field("local", alias="SOVEREIGN_ENVIRONMENT")
|
|
437
|
+
debug_enabled: bool = Field(False, alias="SOVEREIGN_DEBUG_ENABLED")
|
|
438
|
+
sentry_dsn: str = Field("", alias="SOVEREIGN_SENTRY_DSN")
|
|
439
|
+
node_match_key: str = Field("cluster", alias="SOVEREIGN_NODE_MATCH_KEY")
|
|
440
|
+
node_matching: bool = Field(True, alias="SOVEREIGN_NODE_MATCHING")
|
|
441
|
+
source_match_key: str = Field(
|
|
442
|
+
"service_clusters", alias="SOVEREIGN_SOURCE_MATCH_KEY"
|
|
443
|
+
)
|
|
444
|
+
sources_refresh_rate: int = Field(30, alias="SOVEREIGN_SOURCES_REFRESH_RATE")
|
|
445
|
+
cache_strategy: str = Field("context", alias="SOVEREIGN_CACHE_STRATEGY")
|
|
446
|
+
refresh_context: bool = Field(False, alias="SOVEREIGN_REFRESH_CONTEXT")
|
|
447
|
+
context_refresh_rate: Optional[int] = Field(
|
|
448
|
+
None, alias="SOVEREIGN_CONTEXT_REFRESH_RATE"
|
|
449
|
+
)
|
|
450
|
+
context_refresh_cron: Optional[str] = Field(
|
|
451
|
+
None, alias="SOVEREIGN_CONTEXT_REFRESH_CRON"
|
|
452
|
+
)
|
|
453
|
+
dns_hard_fail: bool = Field(False, alias="SOVEREIGN_DNS_HARD_FAIL")
|
|
454
|
+
enable_application_logs: bool = Field(
|
|
455
|
+
True, alias="SOVEREIGN_ENABLE_APPLICATION_LOGS"
|
|
456
|
+
)
|
|
457
|
+
enable_access_logs: bool = Field(True, alias="SOVEREIGN_ENABLE_ACCESS_LOGS")
|
|
458
|
+
log_fmt: Optional[str] = Field("", alias="SOVEREIGN_LOG_FORMAT")
|
|
459
|
+
ignore_empty_log_fields: bool = Field(False, alias="SOVEREIGN_LOG_IGNORE_EMPTY")
|
|
432
460
|
discovery_cache: DiscoveryCacheConfig = DiscoveryCacheConfig()
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
"environment": {"env": "SOVEREIGN_ENVIRONMENT"},
|
|
440
|
-
"debug_enabled": {"env": "SOVEREIGN_DEBUG_ENABLED"},
|
|
441
|
-
"sentry_dsn": {"env": "SOVEREIGN_SENTRY_DSN"},
|
|
442
|
-
"node_match_key": {"env": "SOVEREIGN_NODE_MATCH_KEY"},
|
|
443
|
-
"node_matching": {"env": "SOVEREIGN_NODE_MATCHING"},
|
|
444
|
-
"source_match_key": {"env": "SOVEREIGN_SOURCE_MATCH_KEY"},
|
|
445
|
-
"sources_refresh_rate": {"env": "SOVEREIGN_SOURCES_REFRESH_RATE"},
|
|
446
|
-
"cache_strategy": {"env": "SOVEREIGN_CACHE_STRATEGY"},
|
|
447
|
-
"refresh_context": {"env": "SOVEREIGN_REFRESH_CONTEXT"},
|
|
448
|
-
"context_refresh_rate": {"env": "SOVEREIGN_CONTEXT_REFRESH_RATE"},
|
|
449
|
-
"context_refresh_cron": {"env": "SOVEREIGN_CONTEXT_REFRESH_CRON"},
|
|
450
|
-
"dns_hard_fail": {"env": "SOVEREIGN_DNS_HARD_FAIL"},
|
|
451
|
-
"enable_application_logs": {"env": "SOVEREIGN_ENABLE_APPLICATION_LOGS"},
|
|
452
|
-
"enable_access_logs": {"env": "SOVEREIGN_ENABLE_ACCESS_LOGS"},
|
|
453
|
-
"log_fmt": {"env": "SOVEREIGN_LOG_FORMAT"},
|
|
454
|
-
"ignore_empty_fields": {"env": "SOVEREIGN_LOG_IGNORE_EMPTY"},
|
|
455
|
-
}
|
|
461
|
+
model_config = SettingsConfigDict(
|
|
462
|
+
env_file=".env",
|
|
463
|
+
extra="ignore",
|
|
464
|
+
env_file_encoding="utf-8",
|
|
465
|
+
populate_by_name=True,
|
|
466
|
+
)
|
|
456
467
|
|
|
457
468
|
@property
|
|
458
469
|
def passwords(self) -> List[str]:
|
|
@@ -492,16 +503,15 @@ class TemplateSpecification(BaseModel):
|
|
|
492
503
|
|
|
493
504
|
|
|
494
505
|
class NodeMatching(BaseSettings):
|
|
495
|
-
enabled: bool = True
|
|
496
|
-
source_key: str = "service_clusters"
|
|
497
|
-
node_key: str = "cluster"
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
506
|
+
enabled: bool = Field(True, alias="SOVEREIGN_NODE_MATCHING_ENABLED")
|
|
507
|
+
source_key: str = Field("service_clusters", alias="SOVEREIGN_SOURCE_MATCH_KEY")
|
|
508
|
+
node_key: str = Field("cluster", alias="SOVEREIGN_NODE_MATCH_KEY")
|
|
509
|
+
model_config = SettingsConfigDict(
|
|
510
|
+
env_file=".env",
|
|
511
|
+
extra="ignore",
|
|
512
|
+
env_file_encoding="utf-8",
|
|
513
|
+
populate_by_name=True,
|
|
514
|
+
)
|
|
505
515
|
|
|
506
516
|
|
|
507
517
|
@dataclass
|
|
@@ -511,9 +521,15 @@ class EncryptionConfig:
|
|
|
511
521
|
|
|
512
522
|
|
|
513
523
|
class AuthConfiguration(BaseSettings):
|
|
514
|
-
enabled: bool = False
|
|
515
|
-
auth_passwords: SecretStr = SecretStr("")
|
|
516
|
-
encryption_key: SecretStr = SecretStr("")
|
|
524
|
+
enabled: bool = Field(False, alias="SOVEREIGN_AUTH_ENABLED")
|
|
525
|
+
auth_passwords: SecretStr = Field(SecretStr(""), alias="SOVEREIGN_AUTH_PASSWORDS")
|
|
526
|
+
encryption_key: SecretStr = Field(SecretStr(""), alias="SOVEREIGN_ENCRYPTION_KEY")
|
|
527
|
+
model_config = SettingsConfigDict(
|
|
528
|
+
env_file=".env",
|
|
529
|
+
extra="ignore",
|
|
530
|
+
env_file_encoding="utf-8",
|
|
531
|
+
populate_by_name=True,
|
|
532
|
+
)
|
|
517
533
|
|
|
518
534
|
@staticmethod
|
|
519
535
|
def _create_encryption_config(encryption_key_setting: str) -> EncryptionConfig:
|
|
@@ -534,37 +550,29 @@ class AuthConfiguration(BaseSettings):
|
|
|
534
550
|
)
|
|
535
551
|
return configs
|
|
536
552
|
|
|
537
|
-
class Config:
|
|
538
|
-
fields = {
|
|
539
|
-
"enabled": {"env": "SOVEREIGN_AUTH_ENABLED"},
|
|
540
|
-
"auth_passwords": {"env": "SOVEREIGN_AUTH_PASSWORDS"},
|
|
541
|
-
"encryption_key": {"env": "SOVEREIGN_ENCRYPTION_KEY"},
|
|
542
|
-
}
|
|
543
|
-
|
|
544
553
|
|
|
545
554
|
class ApplicationLogConfiguration(BaseSettings):
|
|
546
|
-
enabled: bool = False
|
|
547
|
-
log_fmt: Optional[str] = None
|
|
555
|
+
enabled: bool = Field(False, alias="SOVEREIGN_ENABLE_APPLICATION_LOGS")
|
|
556
|
+
log_fmt: Optional[str] = Field(None, alias="SOVEREIGN_APPLICATION_LOG_FORMAT")
|
|
548
557
|
# currently only support /dev/stdout as JSON
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
558
|
+
model_config = SettingsConfigDict(
|
|
559
|
+
env_file=".env",
|
|
560
|
+
extra="ignore",
|
|
561
|
+
env_file_encoding="utf-8",
|
|
562
|
+
populate_by_name=True,
|
|
563
|
+
)
|
|
555
564
|
|
|
556
565
|
|
|
557
566
|
class AccessLogConfiguration(BaseSettings):
|
|
558
|
-
enabled: bool = True
|
|
559
|
-
log_fmt: Optional[str] = None
|
|
560
|
-
ignore_empty_fields: bool = False
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
567
|
+
enabled: bool = Field(True, alias="SOVEREIGN_ENABLE_ACCESS_LOGS")
|
|
568
|
+
log_fmt: Optional[str] = Field(None, alias="SOVEREIGN_LOG_FORMAT")
|
|
569
|
+
ignore_empty_fields: bool = Field(False, alias="SOVEREIGN_LOG_IGNORE_EMPTY")
|
|
570
|
+
model_config = SettingsConfigDict(
|
|
571
|
+
env_file=".env",
|
|
572
|
+
extra="ignore",
|
|
573
|
+
env_file_encoding="utf-8",
|
|
574
|
+
populate_by_name=True,
|
|
575
|
+
)
|
|
568
576
|
|
|
569
577
|
|
|
570
578
|
class LoggingConfiguration(BaseSettings):
|
|
@@ -574,11 +582,19 @@ class LoggingConfiguration(BaseSettings):
|
|
|
574
582
|
|
|
575
583
|
class ContextConfiguration(BaseSettings):
|
|
576
584
|
context: Dict[str, Loadable] = {}
|
|
577
|
-
refresh: bool = False
|
|
578
|
-
refresh_rate: Optional[int] = None
|
|
579
|
-
refresh_cron: Optional[str] = None
|
|
580
|
-
refresh_num_retries: int = 3
|
|
581
|
-
refresh_retry_interval_secs: int =
|
|
585
|
+
refresh: bool = Field(False, alias="SOVEREIGN_REFRESH_CONTEXT")
|
|
586
|
+
refresh_rate: Optional[int] = Field(None, alias="SOVEREIGN_CONTEXT_REFRESH_RATE")
|
|
587
|
+
refresh_cron: Optional[str] = Field(None, alias="SOVEREIGN_CONTEXT_REFRESH_CRON")
|
|
588
|
+
refresh_num_retries: int = Field(3, alias="SOVEREIGN_CONTEXT_REFRESH_NUM_RETRIES")
|
|
589
|
+
refresh_retry_interval_secs: int = Field(
|
|
590
|
+
10, alias="SOVEREIGN_CONTEXT_REFRESH_RETRY_INTERVAL_SECS"
|
|
591
|
+
)
|
|
592
|
+
model_config = SettingsConfigDict(
|
|
593
|
+
env_file=".env",
|
|
594
|
+
extra="ignore",
|
|
595
|
+
env_file_encoding="utf-8",
|
|
596
|
+
populate_by_name=True,
|
|
597
|
+
)
|
|
582
598
|
|
|
583
599
|
@staticmethod
|
|
584
600
|
def context_from_legacy(context: Dict[str, str]) -> Dict[str, Loadable]:
|
|
@@ -587,20 +603,16 @@ class ContextConfiguration(BaseSettings):
|
|
|
587
603
|
ret[key] = Loadable.from_legacy_fmt(value)
|
|
588
604
|
return ret
|
|
589
605
|
|
|
590
|
-
@
|
|
591
|
-
def validate_single_use_refresh_method(
|
|
592
|
-
|
|
593
|
-
) -> Dict[str, Any]:
|
|
594
|
-
refresh_rate = values.get("refresh_rate")
|
|
595
|
-
refresh_cron = values.get("refresh_cron")
|
|
596
|
-
|
|
597
|
-
if (refresh_rate is not None) and (refresh_cron is not None):
|
|
606
|
+
@model_validator(mode="after")
|
|
607
|
+
def validate_single_use_refresh_method(self) -> Self:
|
|
608
|
+
if (self.refresh_rate is not None) and (self.refresh_cron is not None):
|
|
598
609
|
raise RuntimeError(
|
|
599
|
-
f"Only one of SOVEREIGN_CONTEXT_REFRESH_RATE or SOVEREIGN_CONTEXT_REFRESH_CRON can be defined. Got {refresh_rate=} and {refresh_cron=}"
|
|
610
|
+
f"Only one of SOVEREIGN_CONTEXT_REFRESH_RATE or SOVEREIGN_CONTEXT_REFRESH_CRON can be defined. Got {self.refresh_rate=} and {self.refresh_cron=}"
|
|
600
611
|
)
|
|
601
|
-
return
|
|
612
|
+
return self
|
|
602
613
|
|
|
603
|
-
@
|
|
614
|
+
@model_validator(mode="before")
|
|
615
|
+
@classmethod
|
|
604
616
|
def set_default_refresh_rate(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
|
605
617
|
refresh_rate = values.get("refresh_rate")
|
|
606
618
|
refresh_cron = values.get("refresh_cron")
|
|
@@ -609,7 +621,8 @@ class ContextConfiguration(BaseSettings):
|
|
|
609
621
|
values["refresh_rate"] = 3600
|
|
610
622
|
return values
|
|
611
623
|
|
|
612
|
-
@
|
|
624
|
+
@field_validator("refresh_cron")
|
|
625
|
+
@classmethod
|
|
613
626
|
def validate_refresh_cron(cls, v: Optional[str]) -> Optional[str]:
|
|
614
627
|
if v is None:
|
|
615
628
|
return v
|
|
@@ -617,36 +630,34 @@ class ContextConfiguration(BaseSettings):
|
|
|
617
630
|
raise CroniterBadCronError(f"'{v}' is not a valid cron expression")
|
|
618
631
|
return v
|
|
619
632
|
|
|
620
|
-
class Config:
|
|
621
|
-
fields = {
|
|
622
|
-
"refresh": {"env": "SOVEREIGN_REFRESH_CONTEXT"},
|
|
623
|
-
"refresh_rate": {"env": "SOVEREIGN_CONTEXT_REFRESH_RATE"},
|
|
624
|
-
"refresh_cron": {"env": "SOVEREIGN_CONTEXT_REFRESH_CRON"},
|
|
625
|
-
"refresh_num_retries": {"env": "SOVEREIGN_CONTEXT_REFRESH_NUM_RETRIES"},
|
|
626
|
-
"refresh_retry_interval_secs": {
|
|
627
|
-
"env": "SOVEREIGN_CONTEXT_REFRESH_RETRY_INTERVAL_SECS"
|
|
628
|
-
},
|
|
629
|
-
}
|
|
630
|
-
|
|
631
633
|
|
|
632
634
|
class SourcesConfiguration(BaseSettings):
|
|
633
|
-
refresh_rate: int = 30
|
|
634
|
-
cache_strategy: CacheStrategy =
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
635
|
+
refresh_rate: int = Field(30, alias="SOVEREIGN_SOURCES_REFRESH_RATE")
|
|
636
|
+
cache_strategy: CacheStrategy = Field(
|
|
637
|
+
CacheStrategy.context, alias="SOVEREIGN_CACHE_STRATEGY"
|
|
638
|
+
)
|
|
639
|
+
model_config = SettingsConfigDict(
|
|
640
|
+
env_file=".env",
|
|
641
|
+
extra="ignore",
|
|
642
|
+
env_file_encoding="utf-8",
|
|
643
|
+
populate_by_name=True,
|
|
644
|
+
)
|
|
641
645
|
|
|
642
646
|
|
|
643
647
|
class LegacyConfig(BaseSettings):
|
|
644
648
|
regions: Optional[List[str]] = None
|
|
645
|
-
eds_priority_matrix: Optional[Dict[str, Dict[str,
|
|
646
|
-
dns_hard_fail: Optional[bool] = None
|
|
647
|
-
environment: Optional[str] = None
|
|
649
|
+
eds_priority_matrix: Optional[Dict[str, Dict[str, int]]] = None
|
|
650
|
+
dns_hard_fail: Optional[bool] = Field(None, alias="SOVEREIGN_DNS_HARD_FAIL")
|
|
651
|
+
environment: Optional[str] = Field(None, alias="SOVEREIGN_ENVIRONMENT")
|
|
652
|
+
model_config = SettingsConfigDict(
|
|
653
|
+
env_file=".env",
|
|
654
|
+
extra="ignore",
|
|
655
|
+
env_file_encoding="utf-8",
|
|
656
|
+
populate_by_name=True,
|
|
657
|
+
)
|
|
648
658
|
|
|
649
|
-
@
|
|
659
|
+
@field_validator("regions")
|
|
660
|
+
@classmethod
|
|
650
661
|
def regions_is_set(cls, v: Optional[List[str]]) -> List[str]:
|
|
651
662
|
if v is not None:
|
|
652
663
|
warnings.warn(
|
|
@@ -659,7 +670,8 @@ class LegacyConfig(BaseSettings):
|
|
|
659
670
|
else:
|
|
660
671
|
return []
|
|
661
672
|
|
|
662
|
-
@
|
|
673
|
+
@field_validator("eds_priority_matrix")
|
|
674
|
+
@classmethod
|
|
663
675
|
def eds_priority_matrix_is_set(
|
|
664
676
|
cls, v: Optional[Dict[str, Dict[str, Any]]]
|
|
665
677
|
) -> Dict[str, Dict[str, Any]]:
|
|
@@ -674,7 +686,8 @@ class LegacyConfig(BaseSettings):
|
|
|
674
686
|
else:
|
|
675
687
|
return {}
|
|
676
688
|
|
|
677
|
-
@
|
|
689
|
+
@field_validator("dns_hard_fail")
|
|
690
|
+
@classmethod
|
|
678
691
|
def dns_hard_fail_is_set(cls, v: Optional[bool]) -> bool:
|
|
679
692
|
if v is not None:
|
|
680
693
|
warnings.warn(
|
|
@@ -688,7 +701,8 @@ class LegacyConfig(BaseSettings):
|
|
|
688
701
|
else:
|
|
689
702
|
return False
|
|
690
703
|
|
|
691
|
-
@
|
|
704
|
+
@field_validator("environment")
|
|
705
|
+
@classmethod
|
|
692
706
|
def environment_is_set(cls, v: Optional[str]) -> Optional[str]:
|
|
693
707
|
if v is not None:
|
|
694
708
|
warnings.warn(
|
|
@@ -701,12 +715,6 @@ class LegacyConfig(BaseSettings):
|
|
|
701
715
|
else:
|
|
702
716
|
return None
|
|
703
717
|
|
|
704
|
-
class Config:
|
|
705
|
-
fields = {
|
|
706
|
-
"dns_hard_fail": {"env": "SOVEREIGN_DNS_HARD_FAIL"},
|
|
707
|
-
"environment": {"env": "SOVEREIGN_ENVIRONMENT"},
|
|
708
|
-
}
|
|
709
|
-
|
|
710
718
|
|
|
711
719
|
class SovereignConfigv2(BaseSettings):
|
|
712
720
|
sources: List[ConfiguredSource]
|
|
@@ -719,16 +727,16 @@ class SovereignConfigv2(BaseSettings):
|
|
|
719
727
|
authentication: AuthConfiguration = AuthConfiguration()
|
|
720
728
|
logging: LoggingConfiguration = LoggingConfiguration()
|
|
721
729
|
statsd: StatsdConfig = StatsdConfig()
|
|
722
|
-
sentry_dsn: SecretStr = SecretStr("")
|
|
723
|
-
debug: bool = False
|
|
730
|
+
sentry_dsn: SecretStr = Field(SecretStr(""), alias="SOVEREIGN_SENTRY_DSN")
|
|
731
|
+
debug: bool = Field(False, alias="SOVEREIGN_DEBUG")
|
|
724
732
|
legacy_fields: LegacyConfig = LegacyConfig()
|
|
725
733
|
discovery_cache: DiscoveryCacheConfig = DiscoveryCacheConfig()
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
734
|
+
model_config = SettingsConfigDict(
|
|
735
|
+
env_file=".env",
|
|
736
|
+
extra="ignore",
|
|
737
|
+
env_file_encoding="utf-8",
|
|
738
|
+
populate_by_name=True,
|
|
739
|
+
)
|
|
732
740
|
|
|
733
741
|
@property
|
|
734
742
|
def passwords(self) -> List[str]:
|
|
@@ -751,10 +759,10 @@ class SovereignConfigv2(BaseSettings):
|
|
|
751
759
|
return self.__repr__()
|
|
752
760
|
|
|
753
761
|
def __repr__(self) -> str:
|
|
754
|
-
return f"SovereignConfigv2({self.
|
|
762
|
+
return f"SovereignConfigv2({self.model_dump()})"
|
|
755
763
|
|
|
756
764
|
def show(self) -> Dict[str, Any]:
|
|
757
|
-
return self.
|
|
765
|
+
return self.model_dump()
|
|
758
766
|
|
|
759
767
|
@staticmethod
|
|
760
768
|
def from_legacy_config(other: SovereignConfig) -> "SovereignConfigv2":
|
|
@@ -88,14 +88,24 @@
|
|
|
88
88
|
1: 'resource'
|
|
89
89
|
} %}
|
|
90
90
|
{% for resource in res %}
|
|
91
|
+
{% if "sovereign_error" in resource %}
|
|
92
|
+
<span class="panel-icon">
|
|
93
|
+
<i class="fas fa-arrow-right" aria-hidden="true"></i>
|
|
94
|
+
</span>
|
|
95
|
+
<div class="notification is-danger">
|
|
96
|
+
{{ resource["sovereign_error"] }}
|
|
97
|
+
</div>
|
|
98
|
+
{{ resource["sovereign_error"] }}
|
|
99
|
+
{% else %}
|
|
91
100
|
{% set name = resource.get('name') or resource['cluster_name'] %}
|
|
92
101
|
<a class="panel-block has-text-weight-medium"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
href="/ui/resources/{{ resource_type }}/{{ name }}">
|
|
103
|
+
<span class="panel-icon">
|
|
104
|
+
<i class="fas fa-arrow-right" aria-hidden="true"></i>
|
|
105
|
+
</span>
|
|
106
|
+
{{ name }}
|
|
107
|
+
</a>
|
|
108
|
+
{% endif %}
|
|
99
109
|
{% endfor %}
|
|
100
110
|
<div class="panel-block">
|
|
101
111
|
<p class="content is-small">
|
|
@@ -16,7 +16,7 @@ router = APIRouter()
|
|
|
16
16
|
@router.get("/xds_dump", summary="Displays all xDS resources as JSON")
|
|
17
17
|
async def display_config(
|
|
18
18
|
xds_type: str = Query(
|
|
19
|
-
..., title="xDS type", description="The type of request",
|
|
19
|
+
..., title="xDS type", description="The type of request", examples=["clusters"]
|
|
20
20
|
),
|
|
21
21
|
service_cluster: str = Query(
|
|
22
22
|
"*", title="The clients service cluster to emulate in this XDS request"
|
|
@@ -48,7 +48,7 @@ async def display_config(
|
|
|
48
48
|
)
|
|
49
49
|
async def debug_template(
|
|
50
50
|
xds_type: str = Query(
|
|
51
|
-
..., title="xDS type", description="The type of request",
|
|
51
|
+
..., title="xDS type", description="The type of request", examples=["clusters"]
|
|
52
52
|
),
|
|
53
53
|
service_cluster: str = Query(
|
|
54
54
|
"*", title="The clients service cluster to emulate in this XDS request"
|
|
@@ -103,8 +103,8 @@ async def resources(
|
|
|
103
103
|
resource_type=xds_type,
|
|
104
104
|
skip_auth=True,
|
|
105
105
|
)
|
|
106
|
-
except KeyError:
|
|
107
|
-
ret["resources"] = []
|
|
106
|
+
except KeyError as e:
|
|
107
|
+
ret["resources"] = [{"sovereign_error": str(e)}]
|
|
108
108
|
else:
|
|
109
109
|
ret["resources"] += response.deserialize_resources()
|
|
110
110
|
return html_templates.TemplateResponse(
|
|
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
|