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.

Files changed (65) hide show
  1. sovereign/__init__.py +2 -2
  2. sovereign/app.py +3 -6
  3. sovereign/cache/__init__.py +12 -85
  4. sovereign/cache/backends/__init__.py +1 -1
  5. sovereign/cache/backends/s3.py +6 -24
  6. sovereign/cache/filesystem.py +5 -6
  7. sovereign/cache/types.py +0 -2
  8. sovereign/configuration.py +8 -42
  9. sovereign/context.py +9 -8
  10. sovereign/dynamic_config/__init__.py +4 -3
  11. sovereign/dynamic_config/deser.py +1 -1
  12. sovereign/dynamic_config/loaders.py +3 -3
  13. sovereign/error_info.py +3 -2
  14. sovereign/events.py +3 -3
  15. sovereign/logging/access_logger.py +1 -1
  16. sovereign/logging/application_logger.py +1 -1
  17. sovereign/logging/bootstrapper.py +1 -1
  18. sovereign/modifiers/lib.py +1 -1
  19. sovereign/rendering.py +90 -22
  20. sovereign/response_class.py +2 -2
  21. sovereign/server.py +26 -45
  22. sovereign/sources/__init__.py +3 -0
  23. sovereign/sources/file.py +21 -0
  24. sovereign/sources/inline.py +39 -0
  25. sovereign/sources/lib.py +41 -0
  26. sovereign/sources/poller.py +537 -0
  27. sovereign/statistics.py +1 -2
  28. sovereign/testing/loaders.py +0 -1
  29. sovereign/tracing.py +5 -6
  30. sovereign/types.py +10 -15
  31. sovereign/utils/auth.py +2 -3
  32. sovereign/utils/crypto/suites/disabled_cipher.py +2 -2
  33. sovereign/utils/dictupdate.py +1 -1
  34. sovereign/utils/eds.py +1 -3
  35. sovereign/utils/entry_point_loader.py +2 -2
  36. sovereign/utils/mock.py +3 -4
  37. sovereign/utils/resources.py +1 -1
  38. sovereign/utils/templates.py +2 -4
  39. sovereign/utils/timer.py +3 -5
  40. sovereign/utils/weighted_clusters.py +1 -2
  41. sovereign/views/__init__.py +3 -6
  42. sovereign/views/api.py +7 -28
  43. sovereign/views/crypto.py +1 -1
  44. sovereign/views/discovery.py +5 -20
  45. sovereign/views/healthchecks.py +27 -45
  46. sovereign/views/interface.py +10 -70
  47. sovereign/worker.py +31 -20
  48. {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/METADATA +3 -4
  49. sovereign-1.0.0b148.dist-info/RECORD +77 -0
  50. {sovereign-1.0.0a4.dist-info → sovereign-1.0.0b148.dist-info}/entry_points.txt +0 -8
  51. sovereign/rendering_common.py +0 -91
  52. sovereign/v2/__init__.py +0 -0
  53. sovereign/v2/data/data_store.py +0 -621
  54. sovereign/v2/data/render_discovery_response.py +0 -24
  55. sovereign/v2/data/repositories.py +0 -90
  56. sovereign/v2/data/utils.py +0 -33
  57. sovereign/v2/data/worker_queue.py +0 -273
  58. sovereign/v2/jobs/refresh_context.py +0 -117
  59. sovereign/v2/jobs/render_discovery_job.py +0 -145
  60. sovereign/v2/logging.py +0 -81
  61. sovereign/v2/types.py +0 -41
  62. sovereign/v2/web.py +0 -101
  63. sovereign/v2/worker.py +0 -199
  64. sovereign-1.0.0a4.dist-info/RECORD +0 -85
  65. {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
@@ -1,6 +1,6 @@
1
- from functools import cached_property
2
- from importlib.metadata import EntryPoints, entry_points
3
1
  from typing import Dict
2
+ from functools import cached_property
3
+ from importlib.metadata import entry_points, EntryPoints
4
4
 
5
5
 
6
6
  class EntryPointLoader:
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 typing import Dict, List, Optional
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
 
@@ -1,5 +1,5 @@
1
- import importlib.resources as res
2
1
  from functools import cache
2
+ import importlib.resources as res
3
3
  from importlib.resources.abc import Traversable
4
4
 
5
5
 
@@ -1,9 +1,7 @@
1
- from socket import gaierror as dns_error
1
+ from typing import List, Optional, Any, Dict
2
2
  from socket import gethostbyname_ex
3
- from typing import Any, Dict, List, Optional
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(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()
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
 
@@ -1,6 +1,5 @@
1
1
  from __future__ import division
2
-
3
- from typing import Any, Dict, Generator, List
2
+ from typing import List, Any, Dict, Generator
4
3
 
5
4
 
6
5
  def _round_to_n(sequence: List[int], n: int = 100) -> List[int]:
@@ -1,7 +1,4 @@
1
- from sovereign import cache, config
1
+ from sovereign import cache
2
2
 
3
- if not config.worker_v2_enabled:
4
- reader = cache.CacheReader()
5
- else:
6
- # don't use the cache reader when in v2 worker mode
7
- reader = None
3
+
4
+ reader = cache.CacheReader()
sovereign/views/api.py CHANGED
@@ -1,14 +1,12 @@
1
1
  import json
2
- from typing import Annotated, Optional
2
+ from typing import Optional
3
3
 
4
- from fastapi import APIRouter, Path, Query
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: Annotated[ConfiguredResourceTypes, Path(title="xDS 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
- entry: Entry | None = None
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
 
@@ -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, config, logs
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 config.worker_v2_enabled:
76
- # we're set up to use v2 of the worker
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)
@@ -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
- from fastapi import Query, Request, Response
6
- from fastapi.responses import JSONResponse, PlainTextResponse
5
+ import pydantic
6
+ from fastapi import Request, Response, Query
7
7
  from fastapi.routing import APIRouter
8
- from typing_extensions import Annotated, Literal
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
- discovery_request = mock_discovery_request(
84
- "v3",
85
- template,
86
- expressions=[f"cluster={envoy_service_cluster}"],
87
- )
88
-
89
- if config.worker_v2_enabled:
90
- # we're set up to use v2 of the worker
91
- response = await wait_for_discovery_response(discovery_request)
92
- if response:
93
- result.templates[template] = "OK"
94
- else:
95
- result.templates[template] = (
96
- "FAIL",
97
- f"Failed to render {template}",
98
- )
99
-
100
- result.worker = "OK"
101
- else:
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
 
@@ -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.cache import Entry
15
- from sovereign.configuration import XDS_TEMPLATES, ConfiguredResourceTypes, config
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
- logger.debug("Making mock request", mock_request=mock_request)
105
-
106
- entry: Entry | None = None
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": entry,
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
- logger.debug("Making mock request", mock_request=mock_request)
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 Body, FastAPI
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.events import Topic, bus
20
- from sovereign.types import DiscoveryRequest, RegisterClientRequest
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
- cid, request = await ONDEMAND.get()
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 {cid} ({request})"
137
+ f"Received on-demand request to render templates for {id} ({request})"
130
138
  )
131
139
  job = rendering.RenderJob(
132
- id=cid, request=request, context=ctx.get_context(request)
140
+ id=id, request=request, context=ctx.get_context(request)
133
141
  )
134
142
  _ = job.submit()
135
- await ONDEMAND.task_done(cid)
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
- client_id, req = writer.register(xds)
192
- ONDEMAND.put_nowait((client_id, req))
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.0a4
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.129,>=0.128.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.50,>=0.49.1
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