sovereign 0.29.0a3__py3-none-any.whl → 0.29.2__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/discovery.py CHANGED
@@ -23,7 +23,6 @@ except ImportError:
23
23
  from sovereign import XDS_TEMPLATES, config, logs, template_context
24
24
  from sovereign.utils.version_info import compute_hash
25
25
  from sovereign.schemas import XdsTemplate, DiscoveryRequest, ProcessedTemplate
26
- from sovereign.tracing import Tracer
27
26
 
28
27
 
29
28
  try:
@@ -106,8 +105,7 @@ def response(request: DiscoveryRequest, xds_type: str) -> ProcessedTemplate:
106
105
  resource_names=request.resources,
107
106
  **template_context.get_context(request, template),
108
107
  )
109
- with Tracer("template rendering"):
110
- content = template(**context)
108
+ content = template(**context)
111
109
 
112
110
  # Deserialize YAML output from Jinja2
113
111
  if not template.is_python_source:
@@ -118,19 +116,13 @@ def response(request: DiscoveryRequest, xds_type: str) -> ProcessedTemplate:
118
116
  content = deserialize_config(content)
119
117
 
120
118
  # Early return if the template is identical
121
- with Tracer("nested test"):
122
- with Tracer("hashing"):
123
- config_version = compute_hash(content)
124
- if (
125
- config_version == request.version_info
126
- and not config.discovery_cache.enabled
127
- ):
128
- return ProcessedTemplate(version_info=config_version, resources=[])
129
-
130
- if not isinstance(content, dict):
131
- raise RuntimeError(f"Attempting to filter unstructured data: {content}")
132
- with Tracer("filtering"):
133
- resources = filter_resources(content["resources"], request.resources)
119
+ config_version = compute_hash(content)
120
+ if config_version == request.version_info and not config.discovery_cache.enabled:
121
+ return ProcessedTemplate(version_info=config_version, resources=[])
122
+
123
+ if not isinstance(content, dict):
124
+ raise RuntimeError(f"Attempting to filter unstructured data: {content}")
125
+ resources = filter_resources(content["resources"], request.resources)
134
126
  return ProcessedTemplate(resources=resources, version_info=config_version)
135
127
 
136
128
 
@@ -1,3 +1,4 @@
1
+ import inspect
1
2
  from typing import Any, Dict, Optional
2
3
 
3
4
  from pydantic import BaseModel
@@ -13,20 +14,20 @@ class Loadable(BaseModel):
13
14
  serialization: Optional[str] = None
14
15
 
15
16
  def load(self, default: Any = None) -> Any:
16
- if self.protocol not in custom_loaders:
17
+ if self.protocol not in LOADERS:
17
18
  raise KeyError(
18
- f"Could not find CustomLoader {self.protocol}. Available: {custom_loaders}"
19
+ f"Could not find CustomLoader {self.protocol}. Available: {LOADERS}"
19
20
  )
20
- loader_ = custom_loaders[self.protocol]
21
+ loader_ = LOADERS[self.protocol]
21
22
 
22
23
  ser = self.serialization
23
24
  if ser is None:
24
25
  ser = loader_.default_deser
25
- if ser not in deserializers:
26
+ if ser not in DESERIALIZERS:
26
27
  raise KeyError(
27
- f"Could not find Deserializer {ser}. Available: {deserializers}"
28
+ f"Could not find Deserializer {ser}. Available: {DESERIALIZERS}"
28
29
  )
29
- deserializer = deserializers[ser]
30
+ deserializer = DESERIALIZERS[ser]
30
31
 
31
32
  try:
32
33
  data = loader_.load(self.path)
@@ -63,23 +64,25 @@ class Loadable(BaseModel):
63
64
  )
64
65
 
65
66
 
66
- custom_loaders: Dict[str, CustomLoader] = {}
67
- loader_entry_point = EntryPointLoader("loaders")
68
- for entry_point in loader_entry_point.groups["loaders"]:
67
+ LOADERS: Dict[str, CustomLoader] = {}
68
+ for entry_point in EntryPointLoader("loaders").groups["loaders"]:
69
69
  custom_loader = entry_point.load()
70
- try:
71
- func = custom_loader()
72
- except AttributeError:
73
- raise AttributeError("CustomLoader does not implement .load()")
74
- custom_loaders[entry_point.name] = func
70
+ func = custom_loader()
71
+ method = getattr(func, "load")
72
+ if not inspect.ismethod(method):
73
+ raise AttributeError(
74
+ f"CustomLoader {entry_point.name} does not implement .load()"
75
+ )
76
+ LOADERS[entry_point.name] = func
75
77
 
76
78
 
77
- deserializers: Dict[str, ConfigDeserializer] = {}
78
- deser_entry_point = EntryPointLoader("deserializers")
79
- for entry_point in deser_entry_point.groups["deserializers"]:
79
+ DESERIALIZERS: Dict[str, ConfigDeserializer] = {}
80
+ for entry_point in EntryPointLoader("deserializers").groups["deserializers"]:
80
81
  deserializer = entry_point.load()
81
- try:
82
- func = deserializer()
83
- except AttributeError:
84
- raise AttributeError("Deserializer does not implement .deserialize()")
85
- deserializers[entry_point.name] = func
82
+ func = deserializer()
83
+ method = getattr(func, "deserialize")
84
+ if not inspect.ismethod(method):
85
+ raise AttributeError(
86
+ f"Deserializer {entry_point.name} does not implement .deserialize()"
87
+ )
88
+ DESERIALIZERS[entry_point.name] = func
sovereign/schemas.py CHANGED
@@ -112,6 +112,7 @@ class DiscoveryCacheConfig(BaseModel):
112
112
  suppress: bool = False # False = Don't suppress connection errors. True = suppress connection errors
113
113
  socket_keepalive: bool = True # Try to keep connections to redis around.
114
114
  ttl: int = 60
115
+ extra_keys: Dict[str, Any] = {}
115
116
 
116
117
  @model_validator(mode="after")
117
118
  def set_default_protocol(self) -> Self:
@@ -294,9 +295,7 @@ class Resources(List[str]):
294
295
  """
295
296
 
296
297
  def __contains__(self, item: object) -> bool:
297
- if (
298
- len(self) == 0
299
- ): # TODO: refactor to remove overriding __contains__; its being used in multiple places
298
+ if len(self) == 0:
300
299
  return True
301
300
  return super().__contains__(item)
302
301
 
@@ -316,7 +315,7 @@ class DiscoveryRequest(BaseModel):
316
315
  version_info: str = Field(
317
316
  "0", title="The version of the envoy clients current configuration"
318
317
  )
319
- resource_names: Resources = Field(
318
+ resource_names: List[str] = Field(
320
319
  default_factory=resources_factory, title="List of requested resource names"
321
320
  )
322
321
  hide_private_keys: bool = False
@@ -117,21 +117,24 @@ async def perform_discovery(
117
117
  authenticate(req)
118
118
  if discovery_cache.enabled:
119
119
  logs.access_logger.queue_log_fields(CACHE_XDS_HIT=False)
120
- cache_key = compute_hash(
121
- [
122
- api_version,
123
- resource_type,
124
- req.envoy_version,
125
- req.resource_names,
126
- req.desired_controlplane,
127
- req.hide_private_keys,
128
- req.type_url,
129
- req.node.cluster,
130
- req.node.locality,
131
- req.node.metadata.get("auth", None),
132
- req.node.metadata.get("num_cpus", None),
133
- ]
134
- )
120
+ metadata_keys = discovery_cache.extra_keys.get("metadata", [])
121
+ extra_metadata = [req.node.metadata.get(key, None) for key in metadata_keys]
122
+ hash_keys = [
123
+ api_version,
124
+ resource_type,
125
+ req.envoy_version,
126
+ req.resource_names,
127
+ req.desired_controlplane,
128
+ req.hide_private_keys,
129
+ req.type_url,
130
+ req.node.cluster,
131
+ req.node.locality,
132
+ req.node.metadata.get("auth", None),
133
+ req.node.metadata.get("num_cpus", None),
134
+ ] + extra_metadata
135
+
136
+ cache_key = compute_hash(hash_keys)
137
+
135
138
  if template := await cache.get(key=cache_key, default=None):
136
139
  logs.access_logger.queue_log_fields(CACHE_XDS_HIT=True)
137
140
  return template # type: ignore[no-any-return]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sovereign
3
- Version: 0.29.0a3
3
+ Version: 0.29.2
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
@@ -18,6 +18,7 @@ Classifier: Natural Language :: English
18
18
  Classifier: Operating System :: POSIX :: Linux
19
19
  Classifier: Programming Language :: Python :: 3
20
20
  Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
21
22
  Classifier: Programming Language :: Python :: 3.10
22
23
  Classifier: Programming Language :: Python :: 3.8
23
24
  Classifier: Programming Language :: Python :: 3.9
@@ -37,7 +38,7 @@ Requires-Dist: cachelib (>=0.10.2,<0.11.0)
37
38
  Requires-Dist: cachetools (>=5.3.2,<6.0.0)
38
39
  Requires-Dist: cashews[redis] (>=6.3.0,<7.0.0) ; extra == "caching"
39
40
  Requires-Dist: croniter (>=1.4.1,<2.0.0)
40
- Requires-Dist: cryptography (>=42.0.5,<43.0.0)
41
+ Requires-Dist: cryptography (>=42.0.0,<43.0.0)
41
42
  Requires-Dist: datadog (>=0.47.0,<0.48.0) ; extra == "statsd"
42
43
  Requires-Dist: fastapi (>=0.110.0,<0.111.0)
43
44
  Requires-Dist: glom (>=23.3.0,<24.0.0)
@@ -48,7 +49,7 @@ Requires-Dist: pydantic (>=2.7.2,<3.0.0)
48
49
  Requires-Dist: pydantic-settings (>=2.3.1,<3.0.0)
49
50
  Requires-Dist: redis (<=5.0.0)
50
51
  Requires-Dist: requests (>=2.31.0,<3.0.0)
51
- Requires-Dist: sentry-sdk (>=1.23.1,<2.0.0) ; extra == "sentry"
52
+ Requires-Dist: sentry-sdk (>=2.14.0,<3.0.0) ; extra == "sentry"
52
53
  Requires-Dist: structlog (>=23.1.0,<24.0.0)
53
54
  Requires-Dist: ujson (>=5.8.0,<6.0.0) ; extra == "ujson"
54
55
  Requires-Dist: uvicorn (>=0.23.2,<0.24.0)
@@ -3,8 +3,8 @@ sovereign/app.py,sha256=udDhuprAcJdYNgXufl94-oh-G74H9hXVzr8cKhgdQYI,4087
3
3
  sovereign/configuration.py,sha256=BCezlWYIpTsFRZwQIBwU-XrfBk1MdjTKMLA8huN-VPg,2484
4
4
  sovereign/constants.py,sha256=qdWD1lTvkaW5JGF7TmZhfksQHlRAJFVqbG7v6JQA9k8,46
5
5
  sovereign/context.py,sha256=Vwr-Jmo1I_05rFraxv6GoYEC83oFl87LG16px4R7IfA,6461
6
- sovereign/discovery.py,sha256=iS34aeJHSTqGY4i0jIQG9Yd0LKS63VQELaMSyvJk6Y8,6198
7
- sovereign/dynamic_config/__init__.py,sha256=kxthEPcB-fiVZ-5qudkct5bphsAo3LuGAE7rU0J9M7Q,2781
6
+ sovereign/discovery.py,sha256=KHnWXUlceUglbR9eV5Q6LjPOpGv2dcrGOSus9-QLQJM,5952
7
+ sovereign/dynamic_config/__init__.py,sha256=QoRNcuCAqV26zeyHm0iavsR55K3TwMohabWpPGIq_rM,2838
8
8
  sovereign/dynamic_config/deser.py,sha256=CYTP9UNx8falCXU_bEaWGNatyQlYrV4T57NPXNhTn0o,1842
9
9
  sovereign/dynamic_config/loaders.py,sha256=HxDT-6hlqg_ewPjrFu2RaWi6O1mmJ_Mpnu8AQk_enNg,2923
10
10
  sovereign/error_info.py,sha256=r2KXBYq9Fo7AI2pmIpATWFm0pykr2MqfrKH0WWW5Sfk,1488
@@ -17,7 +17,7 @@ sovereign/middlewares.py,sha256=UoLdfhqMj_E6jXgtr-n0maQIBYe9n95s3BwaQZfebHo,3097
17
17
  sovereign/modifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  sovereign/modifiers/lib.py,sha256=DbXsxrrjnFE4Y7rbwpeiM5tS5w5NBwSdYH58AtDTP0I,2884
19
19
  sovereign/response_class.py,sha256=beMAFV-4L6DwyWzJzy71GkEW4gb7fzH1jd8-Tul13cU,427
20
- sovereign/schemas.py,sha256=wikM9mt42lbbxKEC60N8emG8fLrwnDp9ikkbJMECGXQ,31516
20
+ sovereign/schemas.py,sha256=U0PDgTPz1cY92sHyI-WzncyxXY3YjlpNhyAy1d1w7Yk,31441
21
21
  sovereign/server.py,sha256=z8Uz1UYIZix0S40Srk774WIMDN2jl2SozO8irib0wc4,1402
22
22
  sovereign/sources/__init__.py,sha256=g9hEpFk8j5i1ApHQpbc9giTyJW41Ppgsqv5P9zGxOJk,78
23
23
  sovereign/sources/file.py,sha256=prUThsDCSPNwZaZpkKXhAm-GVRZWbBoGKGU0It4HHXs,690
@@ -55,11 +55,11 @@ sovereign/utils/weighted_clusters.py,sha256=bPzuRE7Qgvv04HcR2AhMDvBrFlZ8AfteweLK
55
55
  sovereign/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  sovereign/views/admin.py,sha256=9jUI3YqaU42AtzCCOCKDcfj_2JXoaMU6eOAD6WYPjoI,4312
57
57
  sovereign/views/crypto.py,sha256=o8NSyiUBy7v1pMOXt_1UBi68FNcGkXSlEVg9C18y8kY,3324
58
- sovereign/views/discovery.py,sha256=TVvWTMzWydsC-SNKL9WsSss_Hfnt2Ed4SVC2A8Na7Jo,5932
58
+ sovereign/views/discovery.py,sha256=9TyXfS3nW7I7Bkeg-KdsTeCm1N9WSCvDj_pTAi1V4bo,6067
59
59
  sovereign/views/healthchecks.py,sha256=_WkMunlrFpqGTLgtNtRr7gCsDCv5kiuYxCyTi-dMEKM,1357
60
60
  sovereign/views/interface.py,sha256=TFXbYp5oXZPRkVnAo-NWQFBb8XMtB519FaV78ludCcI,7055
61
- sovereign-0.29.0a3.dist-info/LICENSE.txt,sha256=2X125zvAb9AYLjCgdMDQZuufhm0kwcg31A8pGKj_-VY,560
62
- sovereign-0.29.0a3.dist-info/METADATA,sha256=pvrbBMc76iClWyj0-xIP5vcy0T6XTmzBYNCHF9qeEEg,6558
63
- sovereign-0.29.0a3.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
64
- sovereign-0.29.0a3.dist-info/entry_points.txt,sha256=2mUHQjqeXEokMF6ZjDmvqQ9Fxk-Or2S4eC0h70ZxKmk,1201
65
- sovereign-0.29.0a3.dist-info/RECORD,,
61
+ sovereign-0.29.2.dist-info/LICENSE.txt,sha256=2X125zvAb9AYLjCgdMDQZuufhm0kwcg31A8pGKj_-VY,560
62
+ sovereign-0.29.2.dist-info/METADATA,sha256=b_okuWSnbZzVsqLyuvpVIMVyeR2A_5gTruNhzRaKppY,6607
63
+ sovereign-0.29.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
64
+ sovereign-0.29.2.dist-info/entry_points.txt,sha256=2mUHQjqeXEokMF6ZjDmvqQ9Fxk-Or2S4eC0h70ZxKmk,1201
65
+ sovereign-0.29.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.7.0
2
+ Generator: poetry-core 1.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any