sovereign 0.14.2__py3-none-any.whl → 1.0.0a4__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 +17 -78
- sovereign/app.py +74 -59
- sovereign/cache/__init__.py +245 -0
- sovereign/cache/backends/__init__.py +110 -0
- sovereign/cache/backends/s3.py +161 -0
- sovereign/cache/filesystem.py +74 -0
- sovereign/cache/types.py +17 -0
- sovereign/configuration.py +607 -0
- sovereign/constants.py +1 -0
- sovereign/context.py +271 -100
- sovereign/dynamic_config/__init__.py +112 -0
- sovereign/dynamic_config/deser.py +78 -0
- sovereign/dynamic_config/loaders.py +120 -0
- sovereign/error_info.py +61 -0
- sovereign/events.py +49 -0
- sovereign/logging/access_logger.py +85 -0
- sovereign/logging/application_logger.py +54 -0
- sovereign/logging/base_logger.py +41 -0
- sovereign/logging/bootstrapper.py +36 -0
- sovereign/logging/types.py +10 -0
- sovereign/middlewares.py +8 -7
- sovereign/modifiers/lib.py +2 -1
- sovereign/rendering.py +124 -0
- sovereign/rendering_common.py +91 -0
- sovereign/response_class.py +18 -0
- sovereign/server.py +123 -28
- sovereign/statistics.py +19 -21
- sovereign/templates/base.html +59 -46
- sovereign/templates/resources.html +203 -102
- sovereign/testing/loaders.py +9 -0
- sovereign/{modifiers/test.py → testing/modifiers.py} +0 -2
- sovereign/tracing.py +103 -0
- sovereign/types.py +304 -0
- sovereign/utils/auth.py +27 -13
- sovereign/utils/crypto/__init__.py +0 -0
- sovereign/utils/crypto/crypto.py +135 -0
- sovereign/utils/crypto/suites/__init__.py +21 -0
- sovereign/utils/crypto/suites/aes_gcm_cipher.py +42 -0
- sovereign/utils/crypto/suites/base_cipher.py +21 -0
- sovereign/utils/crypto/suites/disabled_cipher.py +25 -0
- sovereign/utils/crypto/suites/fernet_cipher.py +29 -0
- sovereign/utils/dictupdate.py +3 -2
- sovereign/utils/eds.py +40 -22
- sovereign/utils/entry_point_loader.py +18 -0
- sovereign/utils/mock.py +60 -17
- sovereign/utils/resources.py +17 -0
- sovereign/utils/templates.py +4 -2
- sovereign/utils/timer.py +5 -3
- sovereign/utils/version_info.py +8 -0
- sovereign/utils/weighted_clusters.py +2 -1
- sovereign/v2/__init__.py +0 -0
- sovereign/v2/data/data_store.py +621 -0
- sovereign/v2/data/render_discovery_response.py +24 -0
- sovereign/v2/data/repositories.py +90 -0
- sovereign/v2/data/utils.py +33 -0
- sovereign/v2/data/worker_queue.py +273 -0
- sovereign/v2/jobs/refresh_context.py +117 -0
- sovereign/v2/jobs/render_discovery_job.py +145 -0
- sovereign/v2/logging.py +81 -0
- sovereign/v2/types.py +41 -0
- sovereign/v2/web.py +101 -0
- sovereign/v2/worker.py +199 -0
- sovereign/views/__init__.py +7 -0
- sovereign/views/api.py +82 -0
- sovereign/views/crypto.py +46 -15
- sovereign/views/discovery.py +52 -67
- sovereign/views/healthchecks.py +107 -20
- sovereign/views/interface.py +173 -117
- sovereign/worker.py +193 -0
- {sovereign-0.14.2.dist-info → sovereign-1.0.0a4.dist-info}/METADATA +81 -73
- sovereign-1.0.0a4.dist-info/RECORD +85 -0
- {sovereign-0.14.2.dist-info → sovereign-1.0.0a4.dist-info}/WHEEL +1 -1
- sovereign-1.0.0a4.dist-info/entry_points.txt +46 -0
- sovereign_files/__init__.py +0 -0
- sovereign_files/static/darkmode.js +51 -0
- sovereign_files/static/node_expression.js +42 -0
- sovereign_files/static/panel.js +76 -0
- sovereign_files/static/resources.css +246 -0
- sovereign_files/static/resources.js +642 -0
- sovereign_files/static/sass/style.scss +33 -0
- sovereign_files/static/style.css +16143 -0
- sovereign_files/static/style.css.map +1 -0
- sovereign/config_loader.py +0 -225
- sovereign/discovery.py +0 -175
- sovereign/logs.py +0 -131
- sovereign/schemas.py +0 -715
- sovereign/sources/__init__.py +0 -3
- sovereign/sources/file.py +0 -21
- sovereign/sources/inline.py +0 -38
- sovereign/sources/lib.py +0 -40
- sovereign/sources/poller.py +0 -298
- sovereign/static/sass/style.scss +0 -27
- sovereign/static/style.css +0 -13553
- sovereign/templates/ul_filter.html +0 -22
- sovereign/utils/crypto.py +0 -64
- sovereign/views/admin.py +0 -120
- sovereign-0.14.2.dist-info/LICENSE.txt +0 -13
- sovereign-0.14.2.dist-info/RECORD +0 -45
- sovereign-0.14.2.dist-info/entry_points.txt +0 -10
sovereign/utils/eds.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import random
|
|
2
|
-
from typing import Dict, Any, Optional, List
|
|
3
2
|
from copy import deepcopy
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
4
5
|
from starlette.exceptions import HTTPException
|
|
5
|
-
|
|
6
|
-
from sovereign.
|
|
6
|
+
|
|
7
|
+
from sovereign.configuration import config
|
|
8
|
+
from sovereign.types import DiscoveryRequest
|
|
7
9
|
from sovereign.utils.templates import resolve
|
|
8
10
|
|
|
9
11
|
HARD_FAIL_ON_DNS_FAILURE = config.legacy_fields.dns_hard_fail
|
|
@@ -26,12 +28,16 @@ def _upstream_kwargs(
|
|
|
26
28
|
if hard_fail:
|
|
27
29
|
raise
|
|
28
30
|
ip_addresses = [upstream["address"]]
|
|
29
|
-
|
|
31
|
+
ret = {
|
|
30
32
|
"addrs": ip_addresses,
|
|
31
33
|
"port": upstream["port"],
|
|
32
34
|
"region": default_region or upstream.get("region", "unknown"),
|
|
33
35
|
"zone": proxy_region,
|
|
34
36
|
}
|
|
37
|
+
if "health_check_config" in upstream:
|
|
38
|
+
ret["health_check_config"] = upstream["health_check_config"]
|
|
39
|
+
|
|
40
|
+
return ret
|
|
35
41
|
|
|
36
42
|
|
|
37
43
|
def total_zones(endpoints: List[Dict[str, Dict[str, Any]]]) -> int:
|
|
@@ -84,14 +90,21 @@ def locality_lb_endpoints(
|
|
|
84
90
|
return ret
|
|
85
91
|
|
|
86
92
|
|
|
87
|
-
def lb_endpoints(
|
|
93
|
+
def lb_endpoints(
|
|
94
|
+
addrs: List[str],
|
|
95
|
+
port: int,
|
|
96
|
+
region: str,
|
|
97
|
+
zone: str,
|
|
98
|
+
health_check_config: Optional[Dict[str, Any]] = None,
|
|
99
|
+
) -> Dict[str, Any]:
|
|
88
100
|
"""
|
|
89
101
|
Creates an envoy endpoint.LbEndpoints proto
|
|
90
102
|
|
|
91
|
-
:param addrs:
|
|
92
|
-
:param port:
|
|
93
|
-
:param region:
|
|
94
|
-
:param zone:
|
|
103
|
+
:param addrs: The IP addresses or hostname(s) of the upstream.
|
|
104
|
+
:param port: The port that the upstream should be accessed on.
|
|
105
|
+
:param region: The region of the upstream.
|
|
106
|
+
:param zone: The region of the proxy asking for the endpoint configuration.
|
|
107
|
+
:param health_check_config: Optional health check config for the upstream.
|
|
95
108
|
"""
|
|
96
109
|
if PRIORITY_MAPPING is None:
|
|
97
110
|
raise RuntimeError(
|
|
@@ -100,20 +113,25 @@ def lb_endpoints(addrs: List[str], port: int, region: str, zone: str) -> Dict[st
|
|
|
100
113
|
)
|
|
101
114
|
node_priorities = PRIORITY_MAPPING.get(zone, {})
|
|
102
115
|
priority = node_priorities.get(region, 10)
|
|
116
|
+
|
|
117
|
+
endpoints = []
|
|
118
|
+
for addr in addrs:
|
|
119
|
+
endpoint = {
|
|
120
|
+
"endpoint": {
|
|
121
|
+
"address": {
|
|
122
|
+
"socket_address": {
|
|
123
|
+
"address": addr,
|
|
124
|
+
"port_value": port,
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if health_check_config:
|
|
130
|
+
endpoint["endpoint"]["health_check_config"] = health_check_config
|
|
131
|
+
endpoints.append(endpoint)
|
|
132
|
+
|
|
103
133
|
return {
|
|
104
134
|
"priority": priority,
|
|
105
135
|
"locality": {"zone": region},
|
|
106
|
-
"lb_endpoints":
|
|
107
|
-
{
|
|
108
|
-
"endpoint": {
|
|
109
|
-
"address": {
|
|
110
|
-
"socket_address": {
|
|
111
|
-
"address": addr,
|
|
112
|
-
"port_value": port,
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
for addr in addrs
|
|
118
|
-
],
|
|
136
|
+
"lb_endpoints": endpoints,
|
|
119
137
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
from importlib.metadata import EntryPoints, entry_points
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EntryPointLoader:
|
|
7
|
+
ENTRY_POINTS = entry_points()
|
|
8
|
+
|
|
9
|
+
def __init__(self, *args: str) -> None:
|
|
10
|
+
self._groups = args
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def _select(cls, group: str) -> EntryPoints:
|
|
14
|
+
return cls.ENTRY_POINTS.select(group=f"sovereign.{group}")
|
|
15
|
+
|
|
16
|
+
@cached_property
|
|
17
|
+
def groups(self) -> Dict[str, EntryPoints]:
|
|
18
|
+
return {group: self._select(group) for group in self._groups}
|
sovereign/utils/mock.py
CHANGED
|
@@ -1,32 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
import ast
|
|
2
|
+
import re
|
|
2
3
|
from random import randint
|
|
3
|
-
from
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from sovereign.types import DiscoveryRequest, Locality, Node, Status
|
|
7
|
+
|
|
8
|
+
scrub = re.compile(r"[^a-zA-Z_\.]")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NodeExpressionError(Exception):
|
|
12
|
+
pass
|
|
4
13
|
|
|
5
14
|
|
|
6
15
|
def mock_discovery_request(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
api_version: Optional[str] = "V3",
|
|
17
|
+
resource_type: Optional[str] = None,
|
|
18
|
+
resource_names: Optional[List[str] | str] = None,
|
|
19
|
+
region: Optional[str] = "none",
|
|
20
|
+
version: Optional[str] = "<envoy_version>",
|
|
11
21
|
metadata: Optional[Dict[str, str]] = None,
|
|
22
|
+
error_message: Optional[str] = None,
|
|
23
|
+
expressions: Optional[list[str]] = None,
|
|
12
24
|
) -> DiscoveryRequest:
|
|
13
25
|
if resource_names is None:
|
|
14
|
-
resource_names =
|
|
15
|
-
|
|
16
|
-
resource_names =
|
|
26
|
+
resource_names = []
|
|
27
|
+
if isinstance(resource_names, str):
|
|
28
|
+
resource_names = [resource_names]
|
|
29
|
+
if expressions is None:
|
|
30
|
+
expressions = []
|
|
31
|
+
base_node = Node(
|
|
32
|
+
id="sovereign-interface",
|
|
33
|
+
cluster="*",
|
|
34
|
+
build_version=f"<randomHash>/{version}/Clean/RELEASE",
|
|
35
|
+
locality=Locality(zone=region),
|
|
36
|
+
).model_dump()
|
|
37
|
+
set_node_expressions(base_node, expressions)
|
|
17
38
|
request = DiscoveryRequest(
|
|
18
39
|
type_url=None,
|
|
19
|
-
node=Node(
|
|
20
|
-
id="mock",
|
|
21
|
-
cluster=service_cluster or "",
|
|
22
|
-
build_version=f"e5f864a82d4f27110359daa2fbdcb12d99e415b9/{version}/Clean/RELEASE",
|
|
23
|
-
locality=Locality(zone=region, region="example", sub_zone="a"),
|
|
24
|
-
),
|
|
40
|
+
node=Node.model_validate(base_node),
|
|
25
41
|
version_info=str(randint(100000, 1000000000)),
|
|
26
42
|
resource_names=resource_names,
|
|
27
|
-
|
|
28
|
-
desired_controlplane="
|
|
43
|
+
is_internal_request=True,
|
|
44
|
+
desired_controlplane="__sovereign__",
|
|
45
|
+
error_detail=Status(code=200, message="None", details=["None"]),
|
|
46
|
+
api_version=api_version,
|
|
47
|
+
resource_type=resource_type,
|
|
29
48
|
)
|
|
30
49
|
if isinstance(metadata, dict):
|
|
31
50
|
request.node.metadata = metadata
|
|
51
|
+
if error_message:
|
|
52
|
+
request.error_detail = Status(code=666, message=error_message, details=["foo"])
|
|
32
53
|
return request
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def set_node_expressions(node, expressions):
|
|
57
|
+
for expr in expressions:
|
|
58
|
+
try:
|
|
59
|
+
field, value = re.split(r"\s*=\s*", expr, maxsplit=1)
|
|
60
|
+
value = f'"{value}"'
|
|
61
|
+
except ValueError:
|
|
62
|
+
raise NodeExpressionError(f"Invalid node filter format: {expr}")
|
|
63
|
+
|
|
64
|
+
field = scrub.sub("", field)
|
|
65
|
+
parts = field.split(".")
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
value = ast.literal_eval(value)
|
|
69
|
+
except Exception as e:
|
|
70
|
+
raise NodeExpressionError(f"Invalid node filter value: {value}") from e
|
|
71
|
+
|
|
72
|
+
current = node
|
|
73
|
+
for part in parts[:-1]:
|
|
74
|
+
current = current.setdefault(part, {})
|
|
75
|
+
current[parts[-1]] = value
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import importlib.resources as res
|
|
2
|
+
from functools import cache
|
|
3
|
+
from importlib.resources.abc import Traversable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@cache
|
|
7
|
+
def get_package(name: str) -> Traversable:
|
|
8
|
+
return res.files(name)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_package_file(package_name: str, filename: str) -> Traversable:
|
|
12
|
+
return get_package(package_name).joinpath(filename)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_package_file_bytes(package_name: str, filename: str) -> bytes:
|
|
16
|
+
file = get_package_file(package_name, filename)
|
|
17
|
+
return file.read_bytes()
|
sovereign/utils/templates.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from typing import List, Optional, Any, Dict
|
|
2
|
-
from socket import gethostbyname_ex
|
|
3
1
|
from socket import gaierror as dns_error
|
|
2
|
+
from socket import gethostbyname_ex
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
4
5
|
from starlette.exceptions import HTTPException
|
|
6
|
+
|
|
5
7
|
from sovereign import config, stats
|
|
6
8
|
|
|
7
9
|
REGIONS = config.legacy_fields.regions
|
sovereign/utils/timer.py
CHANGED
|
@@ -6,9 +6,11 @@ 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
|
-
|
|
9
|
+
def wait_until(target_dt: datetime) -> float:
|
|
10
|
+
now = datetime.now(dt.timezone.utc)
|
|
11
|
+
if target_dt.tzinfo is None:
|
|
12
|
+
target_dt = target_dt.replace(tzinfo=dt.timezone.utc)
|
|
13
|
+
sleep_time = (target_dt - now).total_seconds()
|
|
12
14
|
return sleep_time
|
|
13
15
|
|
|
14
16
|
|
sovereign/utils/version_info.py
CHANGED
|
@@ -11,3 +11,11 @@ def compute_hash(*args: Any) -> str:
|
|
|
11
11
|
zlib.crc32(data) & 0xFFFFFFFF
|
|
12
12
|
) # same numeric value across all py versions & platforms
|
|
13
13
|
return str(version_info)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def compute_hash_int(*args: Any) -> int:
|
|
17
|
+
data: bytes = repr(args).encode()
|
|
18
|
+
version_info = (
|
|
19
|
+
zlib.crc32(data) & 0xFFFFFFFF
|
|
20
|
+
) # same numeric value across all py versions & platforms
|
|
21
|
+
return version_info
|
sovereign/v2/__init__.py
ADDED
|
File without changes
|