sovereign 1.0.0b125__py3-none-any.whl → 1.0.0b127__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/utils/mock.py CHANGED
@@ -7,6 +7,10 @@ from sovereign.schemas import DiscoveryRequest, Node, Locality, Status
7
7
  scrub = re.compile(r"[^a-zA-Z_\.]")
8
8
 
9
9
 
10
+ class NodeExpressionError(Exception):
11
+ pass
12
+
13
+
10
14
  def mock_discovery_request(
11
15
  api_version: Optional[str] = "V3",
12
16
  resource_type: Optional[str] = None,
@@ -54,7 +58,7 @@ def set_node_expressions(node, expressions):
54
58
  field, value = re.split(r"\s*=\s*", expr, maxsplit=1)
55
59
  value = f'"{value}"'
56
60
  except ValueError:
57
- raise ValueError(f"Invalid expression format: {expr}")
61
+ raise NodeExpressionError(f"Invalid node filter format: {expr}")
58
62
 
59
63
  field = scrub.sub("", field)
60
64
  parts = field.split(".")
@@ -62,7 +66,7 @@ def set_node_expressions(node, expressions):
62
66
  try:
63
67
  value = ast.literal_eval(value)
64
68
  except Exception as e:
65
- raise ValueError(f"invalid value: {value}") from e
69
+ raise NodeExpressionError(f"Invalid node filter value: {value}") from e
66
70
 
67
71
  current = node
68
72
  for part in parts[:-1]:
@@ -7,10 +7,10 @@ from fastapi.encoders import jsonable_encoder
7
7
  from fastapi.requests import Request
8
8
  from fastapi.responses import HTMLResponse, JSONResponse, Response
9
9
 
10
- from sovereign import html_templates, cache
10
+ from sovereign import html_templates, cache, __version__
11
11
  from sovereign.schemas import DiscoveryTypes, XDS_TEMPLATES
12
12
  from sovereign.response_class import json_response_class
13
- from sovereign.utils.mock import mock_discovery_request
13
+ from sovereign.utils.mock import NodeExpressionError, mock_discovery_request
14
14
 
15
15
  router = APIRouter()
16
16
 
@@ -25,9 +25,7 @@ async def ui_main(request: Request) -> HTMLResponse:
25
25
  request=request,
26
26
  name="base.html",
27
27
  media_type="text/html",
28
- context={
29
- "all_types": all_types,
30
- },
28
+ context={"all_types": all_types, "sovereign_version": __version__},
31
29
  )
32
30
  except IndexError:
33
31
  return html_templates.TemplateResponse(
@@ -36,9 +34,12 @@ async def ui_main(request: Request) -> HTMLResponse:
36
34
  media_type="text/html",
37
35
  context={
38
36
  "title": "No resource types configured",
39
- "message": "A template should be defined for every resource "
40
- "type that you want your envoy proxies to discover.",
37
+ "message": (
38
+ "A template should be defined for every resource "
39
+ "type that you want your envoy proxies to discover."
40
+ ),
41
41
  "doc_link": "https://developer.atlassian.com/platform/sovereign/tutorial/templates/#templates",
42
+ "sovereign_version": __version__,
42
43
  },
43
44
  )
44
45
 
@@ -63,18 +64,31 @@ async def resources(
63
64
  ) -> HTMLResponse:
64
65
  ret: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
65
66
  response = None
66
- mock_request = mock_discovery_request(
67
- api_version,
68
- xds_type,
69
- version=envoy_version,
70
- region=region,
71
- expressions=node_expression.split(),
72
- )
67
+ try:
68
+ mock_request = mock_discovery_request(
69
+ api_version,
70
+ xds_type,
71
+ version=envoy_version,
72
+ region=region,
73
+ expressions=node_expression.split(),
74
+ )
75
+ clear_cookie = False
76
+ error = None
77
+ except NodeExpressionError as e:
78
+ mock_request = mock_discovery_request(
79
+ api_version,
80
+ xds_type,
81
+ version=envoy_version,
82
+ region=region,
83
+ )
84
+ clear_cookie = True
85
+ error = str(e)
86
+
73
87
  response = await cache.blocking_read(mock_request)
74
88
  if response:
75
89
  ret["resources"] = json.loads(response.text).get("resources", [])
76
90
 
77
- return html_templates.TemplateResponse(
91
+ resp = html_templates.TemplateResponse(
78
92
  request=request,
79
93
  name="resources.html",
80
94
  media_type="text/html",
@@ -87,8 +101,13 @@ async def resources(
87
101
  "all_types": all_types,
88
102
  "version": envoy_version,
89
103
  "available_versions": list(XDS_TEMPLATES.keys()),
104
+ "error": error,
105
+ "sovereign_version": __version__,
90
106
  },
91
107
  )
108
+ if clear_cookie:
109
+ resp.delete_cookie("node_expression", path="/ui/resources/")
110
+ return resp
92
111
 
93
112
 
94
113
  @router.get(
sovereign/worker.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import asyncio
2
2
  from typing import Optional, final
3
- from multiprocessing import Process, cpu_count
4
3
  from contextlib import asynccontextmanager
5
4
 
6
5
  from fastapi import FastAPI, Body
@@ -61,7 +60,6 @@ class RenderQueue:
61
60
 
62
61
 
63
62
  ONDEMAND = RenderQueue()
64
- RENDER_SEMAPHORE = asyncio.Semaphore(cpu_count())
65
63
 
66
64
 
67
65
  def hidden_field(*args, **kwargs):
@@ -99,42 +97,23 @@ if config.sources is not None:
99
97
  context_middleware.append(poller.add_to_context)
100
98
 
101
99
 
102
- def render(job: rendering.RenderJob):
103
- log.debug(f"Spawning render process for {job.id}")
104
- process = Process(target=rendering.generate, args=[job])
105
- process.start()
106
- return process
107
-
108
-
109
- async def submit_render(job: rendering.RenderJob):
110
- async with RENDER_SEMAPHORE:
111
- process = render(job)
112
- # Wait for the process to complete to ensure semaphore is held
113
- # until the actual rendering work is done
114
- await asyncio.get_event_loop().run_in_executor(None, process.join)
115
-
116
-
117
100
  async def render_on_event():
118
101
  while True:
119
102
  # block forever until new context arrives
120
- await NEW_CONTEXT.wait()
103
+ _ = await NEW_CONTEXT.wait()
121
104
  log.debug("New context detected, re-rendering templates")
122
105
  try:
123
106
  if registered := cache.clients():
124
107
  log.debug("New context detected, re-rendering templates")
125
- jobs = [
126
- rendering.RenderJob(
108
+ size = len(registered)
109
+ stats.increment("template.render_on_event", tags=[f"batch_size:{size}"])
110
+ for client, request in registered:
111
+ job = rendering.RenderJob(
127
112
  id=client,
128
113
  request=request,
129
114
  context=template_context.get_context(request),
130
115
  )
131
- for client, request in registered
132
- ]
133
- tasks = [submit_render(job) for job in jobs]
134
- size = len(tasks)
135
- stats.increment("template.render_on_event", tags=[f"batch_size:{size}"])
136
- await asyncio.gather(*tasks)
137
- log.debug(f"Completed rendering {size} jobs")
116
+ job.spawn()
138
117
  finally:
139
118
  NEW_CONTEXT.clear()
140
119
 
@@ -147,16 +126,24 @@ async def render_on_demand():
147
126
  job = rendering.RenderJob(
148
127
  id=id, request=request, context=template_context.get_context(request)
149
128
  )
150
- await submit_render(job)
129
+ job.spawn()
151
130
  ONDEMAND.task_done()
152
131
 
153
132
 
133
+ async def monitor_render_queue():
134
+ """Periodically report render queue size metrics"""
135
+ while True:
136
+ await asyncio.sleep(10)
137
+ stats.gauge("template.on_demand_queue_size", ONDEMAND._queue.qsize())
138
+
139
+
154
140
  @asynccontextmanager
155
141
  async def lifespan(_: FastAPI):
156
142
  # Template Rendering
157
143
  log.debug("Starting rendering loops")
158
144
  asyncio.create_task(render_on_event())
159
145
  asyncio.create_task(render_on_demand())
146
+ asyncio.create_task(monitor_render_queue())
160
147
 
161
148
  # Template context
162
149
  log.debug("Starting context loop")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sovereign
3
- Version: 1.0.0b125
3
+ Version: 1.0.0b127
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
@@ -15,7 +15,7 @@ sovereign/logging/types.py,sha256=rGqJAEVvgvzHy4aPfvEH6yQ-yblXNkEcWG7G8l9ALEA,28
15
15
  sovereign/middlewares.py,sha256=6w4JpvtNGvQA4rocQsYQjuu-ckhpKT6gKYA16T-kiqA,3082
16
16
  sovereign/modifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  sovereign/modifiers/lib.py,sha256=Cx0VrpTKbSjb3YmHyG4Jy6YEaPlrwpeqNaom3zu1_hw,2885
18
- sovereign/rendering.py,sha256=MIA7se7-C4WTWf7xZSgqpf7NvhDT7NkZbR3_G9N1dHI,5015
18
+ sovereign/rendering.py,sha256=hxlu_K7h1fVNYRLVA4-YM_KY4rhaEOzHq-4dCVwgdWE,5688
19
19
  sovereign/response_class.py,sha256=beMAFV-4L6DwyWzJzy71GkEW4gb7fzH1jd8-Tul13cU,427
20
20
  sovereign/schemas.py,sha256=Jf_w2mAX1b31eCAtJuxSZgy9KMVRKntmnEbbCZGZCxQ,38067
21
21
  sovereign/server.py,sha256=uviXN0mX-Wbhv36xjWlHiybzIlZrJsCwRf4xyRnibXQ,2970
@@ -24,14 +24,16 @@ sovereign/sources/file.py,sha256=prUThsDCSPNwZaZpkKXhAm-GVRZWbBoGKGU0It4HHXs,690
24
24
  sovereign/sources/inline.py,sha256=pP77m7bHjqE3sSoqZthcuw1ARVMf9gooVwbz4B8OAek,1003
25
25
  sovereign/sources/lib.py,sha256=0hk_G6mKJrB65WokVZnqF5kdJ3vsQZMNPuJqJO0mBsI,1031
26
26
  sovereign/sources/poller.py,sha256=zpNUhQft-NoJbbxO1kCFp6jJSRSkBmf181xodnF_TiI,18469
27
- sovereign/static/node_expression.js,sha256=dL9QLM49jorqavf3Qtye6E1QTWYDT1rFI0tQR1HsiLQ,504
27
+ sovereign/static/darkmode.js,sha256=3ip-eKGctDvNhN7UgmaHhzls7r5qIY-Jvh2EpefHbQ0,1449
28
+ sovereign/static/node_expression.js,sha256=GKxKTSRc_96IbL3H4L_31ueJFXq4N7scm5R1RNqxP24,1489
28
29
  sovereign/static/panel.js,sha256=i5mGExjv-I4Gtt9dQiTyFwPZa8pg5rXeuTeidXNUiTE,2695
29
- sovereign/static/sass/style.scss,sha256=tPHPEm3sZeBFGDyyn3pHcA-nbaKT-h-UsSTsf6dHNDU,1158
30
- sovereign/static/style.css,sha256=vG8HPsbCbPIZfHgy7gSeof97Pnp0okkyaXyJzIEEW-8,447517
30
+ sovereign/static/resources.css,sha256=Rt_ir_FkoI-VIAOqPhk0vILy8kB2egAYbQU26SOs1io,4500
31
+ sovereign/static/resources.js,sha256=-TaXZ6tohyKA1SkX5YwrTcV5M8mOZ68cvEXpvZWznTo,24506
32
+ sovereign/static/sass/style.scss,sha256=LdGXXuHi_tyMc7XhijIOrlIxyfLt827AAs2Z7DYpFpg,990
31
33
  sovereign/statistics.py,sha256=QhDB0bs5kZDGjy248AOIv_bzNbz_c2U7xmJ0hoUNOmw,2033
32
- sovereign/templates/base.html,sha256=5vw3-NmN291pXRdArpCwhSce9bAYBWCJVRhvO5EmE9g,2296
34
+ sovereign/templates/base.html,sha256=MMhhvvClTixKibYfhXm8Ezx6ttu6Sqki44niciCPMO4,2990
33
35
  sovereign/templates/err.html,sha256=a3cEzOqyqWOIe3YxfTEjkxbTfxBxq1knD6GwzEFljfs,603
34
- sovereign/templates/resources.html,sha256=QaZ1S38JhAZg3-PfQS1cAKhCczVLXw9e4pztBrqr4qs,40217
36
+ sovereign/templates/resources.html,sha256=5MfXHW8s3tAWda66Q48zVgDhZNLwHGsdCKkKHLZohIs,10420
35
37
  sovereign/testing/loaders.py,sha256=mcmErhI9ZkJUBZl8jv2qP-PCBRFeAIgyBFlfCgU4Vvk,199
36
38
  sovereign/testing/modifiers.py,sha256=7_c2hWXn_sYJ6997N1_uSWtClOikcOzu1yRCY56-l-4,361
37
39
  sovereign/tracing.py,sha256=Xo3npgh6yesACSlynv9j6qnXxvYEBzXv5LL4Zkc1QDw,2446
@@ -47,7 +49,7 @@ sovereign/utils/crypto/suites/fernet_cipher.py,sha256=rP6M5ys1vctyadOxDGNFoyerWP
47
49
  sovereign/utils/dictupdate.py,sha256=Bi7QaC7en-k3EOepwNJqpOKRNBgp6ZsBZVOvH_0nMtc,2558
48
50
  sovereign/utils/eds.py,sha256=sCEDj1y-0Crs40cHZLiPGVb7ed1f8vFqgHLY5R2LMbw,4377
49
51
  sovereign/utils/entry_point_loader.py,sha256=BEVodk-um70RvT1nSOu_IB-hr1K4ppthXod0VZEiZJ8,526
50
- sovereign/utils/mock.py,sha256=VgaxArNnTi0z6cyodrcjiSNdQn4sFOcYG_VQYhvsisI,2308
52
+ sovereign/utils/mock.py,sha256=j9zaLT39MSuF7vGKvhGRJtrXSTU7WFB1rirUWWBGYm4,2388
51
53
  sovereign/utils/resources.py,sha256=rPrWgcIt4YhV-Dz88_kr5WrQNiSKt-jTlOZ8EIJxJx8,472
52
54
  sovereign/utils/templates.py,sha256=FE_H_oE7VrS3X_VN1z_g10b9-rpmi1_gL-cMxi5XtXU,1057
53
55
  sovereign/utils/timer.py,sha256=_dUtEasj0BKbWYuQ_T3HFIyjurXXj-La-dNSMAwKMSo,795
@@ -58,10 +60,10 @@ sovereign/views/api.py,sha256=jKVjSvi0Tr1HHix3hc0H8yGZoyDind2sJ4w7a4pJvy0,2168
58
60
  sovereign/views/crypto.py,sha256=7y0eHWtt-bbr2CwHEkH7odPaJ1IEviU-71U-MYJD0Kc,3360
59
61
  sovereign/views/discovery.py,sha256=B_D1ckfbN1dSKBvuFCTyfB79GUUriCADTB53OwZ8D4Q,2409
60
62
  sovereign/views/healthchecks.py,sha256=TaXbxkX679jyQ8v5FxtBa2Qa0Z7KuqQ10WgAqfuVGUc,1743
61
- sovereign/views/interface.py,sha256=FmQ7LiUPLSvkEDOKCncrnKMD9g1lJKu-DQNbbyi8mqk,6346
62
- sovereign/worker.py,sha256=iMeUQfN6hFoarqteG0eYpzVDj_Izknpe6QnVY-l7U6U,6373
63
- sovereign-1.0.0b125.dist-info/LICENSE.txt,sha256=2X125zvAb9AYLjCgdMDQZuufhm0kwcg31A8pGKj_-VY,560
64
- sovereign-1.0.0b125.dist-info/METADATA,sha256=lE6OWxPME5zhTc3YNQhJbVPxYq4C2rVBQSZJc5JTxlw,6304
65
- sovereign-1.0.0b125.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
66
- sovereign-1.0.0b125.dist-info/entry_points.txt,sha256=VKJdnnN_HNL8xYQMXsFXfFmN6QkdXMEk5S964avxQJI,1404
67
- sovereign-1.0.0b125.dist-info/RECORD,,
63
+ sovereign/views/interface.py,sha256=o6DaOqoh2M09_SsZrCOxr9rCVxMUZHXRXj4TNq800Ho,6999
64
+ sovereign/worker.py,sha256=Y4le54cZU1Enj8scu1G1sM_KPVPUmZ7vmTuxpd59X2Q,5855
65
+ sovereign-1.0.0b127.dist-info/LICENSE.txt,sha256=2X125zvAb9AYLjCgdMDQZuufhm0kwcg31A8pGKj_-VY,560
66
+ sovereign-1.0.0b127.dist-info/METADATA,sha256=oU2lYwJzDy4asWnC88rWcstr-lrjycu0Os6jtrQPAdE,6304
67
+ sovereign-1.0.0b127.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
68
+ sovereign-1.0.0b127.dist-info/entry_points.txt,sha256=VKJdnnN_HNL8xYQMXsFXfFmN6QkdXMEk5S964avxQJI,1404
69
+ sovereign-1.0.0b127.dist-info/RECORD,,