sovereign 1.0.0b124__tar.gz → 1.0.0b126__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-1.0.0b124 → sovereign-1.0.0b126}/PKG-INFO +1 -1
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/pyproject.toml +1 -1
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/rendering.py +46 -29
- sovereign-1.0.0b126/src/sovereign/static/node_expression.js +39 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/templates/resources.html +10 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/mock.py +6 -2
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/views/interface.py +26 -9
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/worker.py +18 -28
- sovereign-1.0.0b124/src/sovereign/static/node_expression.js +0 -16
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/LICENSE.txt +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/README.md +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/app.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/cache.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/constants.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/context.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/dynamic_config/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/dynamic_config/deser.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/dynamic_config/loaders.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/error_info.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/logging/access_logger.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/logging/application_logger.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/logging/base_logger.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/logging/bootstrapper.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/logging/types.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/middlewares.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/modifiers/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/modifiers/lib.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/response_class.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/schemas.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/server.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/sources/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/sources/file.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/sources/inline.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/sources/lib.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/sources/poller.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/static/panel.js +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/static/sass/style.scss +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/static/style.css +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/statistics.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/templates/base.html +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/templates/err.html +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/testing/loaders.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/testing/modifiers.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/tracing.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/auth.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/crypto.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/aes_gcm_cipher.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/base_cipher.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/disabled_cipher.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/fernet_cipher.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/dictupdate.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/eds.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/entry_point_loader.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/resources.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/templates.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/timer.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/version_info.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/weighted_clusters.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/views/__init__.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/views/api.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/views/crypto.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/views/discovery.py +0 -0
- {sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/views/healthchecks.py +0 -0
|
@@ -7,6 +7,8 @@ Functions used to render and return discovery responses to Envoy proxies.
|
|
|
7
7
|
The templates are configurable. `todo See ref:Configuration#Templates`
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import threading
|
|
11
|
+
from multiprocessing import Process, Semaphore, cpu_count
|
|
10
12
|
from typing import Any, Dict, List
|
|
11
13
|
|
|
12
14
|
import yaml
|
|
@@ -27,6 +29,8 @@ from sovereign.schemas import (
|
|
|
27
29
|
ProcessedTemplate,
|
|
28
30
|
)
|
|
29
31
|
|
|
32
|
+
# limit render jobs to number of cores
|
|
33
|
+
RENDER_SEMAPHORE = Semaphore(cpu_count())
|
|
30
34
|
|
|
31
35
|
type_urls = {
|
|
32
36
|
"v2": {
|
|
@@ -54,39 +58,52 @@ class RenderJob(pydantic.BaseModel):
|
|
|
54
58
|
request: DiscoveryRequest
|
|
55
59
|
context: dict[str, Any]
|
|
56
60
|
|
|
61
|
+
def spawn(self):
|
|
62
|
+
t = threading.Thread(target=self._run)
|
|
63
|
+
t.start()
|
|
64
|
+
|
|
65
|
+
def _run(self):
|
|
66
|
+
with RENDER_SEMAPHORE:
|
|
67
|
+
proc = Process(target=generate, args=[self])
|
|
68
|
+
proc.start()
|
|
69
|
+
proc.join()
|
|
70
|
+
|
|
57
71
|
|
|
58
72
|
def generate(job: RenderJob) -> None:
|
|
59
73
|
request = job.request
|
|
60
74
|
tags = [f"type:{request.resource_type}"]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
try:
|
|
76
|
+
with stats.timed("template.render_ms", tags=tags):
|
|
77
|
+
content = request.template(
|
|
78
|
+
discovery_request=request,
|
|
79
|
+
host_header=request.desired_controlplane,
|
|
80
|
+
resource_names=request.resources,
|
|
81
|
+
**job.context,
|
|
82
|
+
)
|
|
83
|
+
if not request.template.is_python_source:
|
|
84
|
+
assert isinstance(content, str)
|
|
85
|
+
content = deserialize_config(content)
|
|
86
|
+
assert isinstance(content, dict)
|
|
87
|
+
resources = filter_resources(content["resources"], request.resources)
|
|
88
|
+
add_type_urls(request.api_version, request.resource_type, resources)
|
|
89
|
+
response = ProcessedTemplate(resources=resources)
|
|
90
|
+
cache.write(
|
|
91
|
+
job.id,
|
|
92
|
+
cache.Entry(
|
|
93
|
+
text=response.model_dump_json(indent=None),
|
|
94
|
+
len=len(response.resources),
|
|
95
|
+
version=response.version_info,
|
|
96
|
+
node=request.node,
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
tags.append("result:ok")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
tags.append("result:err")
|
|
102
|
+
tags.append(f"error:{e.__class__.__name__.lower()}")
|
|
103
|
+
if SENTRY_INSTALLED and config.sentry_dsn:
|
|
104
|
+
sentry_sdk.capture_exception(e)
|
|
105
|
+
finally:
|
|
106
|
+
stats.increment("template.render", tags=tags)
|
|
90
107
|
|
|
91
108
|
|
|
92
109
|
def deserialize_config(content: str) -> Dict[str, Any]:
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const input = document.getElementById('filterInput');
|
|
2
|
+
const inputMessage = document.getElementById('filterMessage');
|
|
3
|
+
const form = document.getElementById('filterForm');
|
|
4
|
+
|
|
5
|
+
function validateInput(inputString) {
|
|
6
|
+
if (!inputString || inputString.trim() === '') {
|
|
7
|
+
return "empty";
|
|
8
|
+
}
|
|
9
|
+
const validationRegex = /^(?:(?:id|cluster|metadata\.[\w\.\=\-]+|locality\.?(?:zone|sub_zone|region))=[a-zA-Z0-9_-]+ ?)*$/;
|
|
10
|
+
return validationRegex.test(inputString);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
14
|
+
const match = document.cookie.match(/(?:^|; )node_expression=([^;]*)/);
|
|
15
|
+
if (match) {
|
|
16
|
+
input.value = match[1];
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
input.addEventListener('input', (event) => {
|
|
21
|
+
const result = validateInput(event.target.value);
|
|
22
|
+
if (result === "empty") {
|
|
23
|
+
input.className = "input is-dark";
|
|
24
|
+
inputMessage.innerHTML = "";
|
|
25
|
+
} else if (result === true) {
|
|
26
|
+
input.className = "input is-success";
|
|
27
|
+
inputMessage.innerHTML = "";
|
|
28
|
+
} else {
|
|
29
|
+
input.className = "input is-danger";
|
|
30
|
+
inputMessage.innerHTML = "The node filter expression may have no effect, or be invalid";
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
form.addEventListener('submit', (event) => {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
const value = input.value.trim();
|
|
37
|
+
document.cookie = `node_expression=${value}; path=/ui/resources/; max-age=31536000`;
|
|
38
|
+
location.reload();
|
|
39
|
+
});
|
|
@@ -271,6 +271,14 @@
|
|
|
271
271
|
|
|
272
272
|
{%- block body %}
|
|
273
273
|
<div class="content">
|
|
274
|
+
{% if error %}
|
|
275
|
+
<span class="panel-icon">
|
|
276
|
+
<i class="fas fa-arrow-right" aria-hidden="true"></i>
|
|
277
|
+
</span>
|
|
278
|
+
<div class="notification is-danger">
|
|
279
|
+
{{ error }}
|
|
280
|
+
</div>
|
|
281
|
+
{% endif %}
|
|
274
282
|
<p class="content">
|
|
275
283
|
<div class="columns">
|
|
276
284
|
<div class="column is-narrow">
|
|
@@ -303,6 +311,7 @@
|
|
|
303
311
|
<form id="filterForm">
|
|
304
312
|
<input id="filterInput" class="input is-dark" type="text" placeholder="Node filter expression"/>
|
|
305
313
|
</form>
|
|
314
|
+
<p id="filterMessage" class="help is-danger"></p>
|
|
306
315
|
</div>
|
|
307
316
|
<div class="column is-narrow">
|
|
308
317
|
<div class="tooltip">
|
|
@@ -322,6 +331,7 @@
|
|
|
322
331
|
</div>
|
|
323
332
|
</div>
|
|
324
333
|
|
|
334
|
+
|
|
325
335
|
{% set count = resources|length %}
|
|
326
336
|
{% if count > 0 %}
|
|
327
337
|
<nav class="panel is-dark" id="resources">
|
|
@@ -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
|
|
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
|
|
69
|
+
raise NodeExpressionError(f"Invalid node filter value: {value}") from e
|
|
66
70
|
|
|
67
71
|
current = node
|
|
68
72
|
for part in parts[:-1]:
|
|
@@ -10,7 +10,7 @@ from fastapi.responses import HTMLResponse, JSONResponse, Response
|
|
|
10
10
|
from sovereign import html_templates, cache
|
|
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
|
|
|
@@ -63,18 +63,31 @@ async def resources(
|
|
|
63
63
|
) -> HTMLResponse:
|
|
64
64
|
ret: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
|
|
65
65
|
response = None
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
try:
|
|
67
|
+
mock_request = mock_discovery_request(
|
|
68
|
+
api_version,
|
|
69
|
+
xds_type,
|
|
70
|
+
version=envoy_version,
|
|
71
|
+
region=region,
|
|
72
|
+
expressions=node_expression.split(),
|
|
73
|
+
)
|
|
74
|
+
clear_cookie = False
|
|
75
|
+
error = None
|
|
76
|
+
except NodeExpressionError as e:
|
|
77
|
+
mock_request = mock_discovery_request(
|
|
78
|
+
api_version,
|
|
79
|
+
xds_type,
|
|
80
|
+
version=envoy_version,
|
|
81
|
+
region=region,
|
|
82
|
+
)
|
|
83
|
+
clear_cookie = True
|
|
84
|
+
error = str(e)
|
|
85
|
+
|
|
73
86
|
response = await cache.blocking_read(mock_request)
|
|
74
87
|
if response:
|
|
75
88
|
ret["resources"] = json.loads(response.text).get("resources", [])
|
|
76
89
|
|
|
77
|
-
|
|
90
|
+
resp = html_templates.TemplateResponse(
|
|
78
91
|
request=request,
|
|
79
92
|
name="resources.html",
|
|
80
93
|
media_type="text/html",
|
|
@@ -87,8 +100,12 @@ async def resources(
|
|
|
87
100
|
"all_types": all_types,
|
|
88
101
|
"version": envoy_version,
|
|
89
102
|
"available_versions": list(XDS_TEMPLATES.keys()),
|
|
103
|
+
"error": error,
|
|
90
104
|
},
|
|
91
105
|
)
|
|
106
|
+
if clear_cookie:
|
|
107
|
+
resp.delete_cookie("node_expression", path="/ui/resources/")
|
|
108
|
+
return resp
|
|
92
109
|
|
|
93
110
|
|
|
94
111
|
@router.get(
|
|
@@ -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
|
|
@@ -56,9 +55,11 @@ class RenderQueue:
|
|
|
56
55
|
def full(self):
|
|
57
56
|
return self._queue.full()
|
|
58
57
|
|
|
58
|
+
def task_done(self):
|
|
59
|
+
self._queue.task_done()
|
|
60
|
+
|
|
59
61
|
|
|
60
62
|
ONDEMAND = RenderQueue()
|
|
61
|
-
RENDER_SEMAPHORE = asyncio.Semaphore(cpu_count())
|
|
62
63
|
|
|
63
64
|
|
|
64
65
|
def hidden_field(*args, **kwargs):
|
|
@@ -96,42 +97,23 @@ if config.sources is not None:
|
|
|
96
97
|
context_middleware.append(poller.add_to_context)
|
|
97
98
|
|
|
98
99
|
|
|
99
|
-
def render(job: rendering.RenderJob):
|
|
100
|
-
log.debug(f"Spawning render process for {job.id}")
|
|
101
|
-
process = Process(target=rendering.generate, args=[job])
|
|
102
|
-
process.start()
|
|
103
|
-
return process
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
async def submit_render(job: rendering.RenderJob):
|
|
107
|
-
async with RENDER_SEMAPHORE:
|
|
108
|
-
process = render(job)
|
|
109
|
-
# Wait for the process to complete to ensure semaphore is held
|
|
110
|
-
# until the actual rendering work is done
|
|
111
|
-
await asyncio.get_event_loop().run_in_executor(None, process.join)
|
|
112
|
-
|
|
113
|
-
|
|
114
100
|
async def render_on_event():
|
|
115
101
|
while True:
|
|
116
102
|
# block forever until new context arrives
|
|
117
|
-
await NEW_CONTEXT.wait()
|
|
103
|
+
_ = await NEW_CONTEXT.wait()
|
|
118
104
|
log.debug("New context detected, re-rendering templates")
|
|
119
105
|
try:
|
|
120
106
|
if registered := cache.clients():
|
|
121
107
|
log.debug("New context detected, re-rendering templates")
|
|
122
|
-
|
|
123
|
-
|
|
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(
|
|
124
112
|
id=client,
|
|
125
113
|
request=request,
|
|
126
114
|
context=template_context.get_context(request),
|
|
127
115
|
)
|
|
128
|
-
|
|
129
|
-
]
|
|
130
|
-
tasks = [submit_render(job) for job in jobs]
|
|
131
|
-
size = len(tasks)
|
|
132
|
-
stats.increment("template.render_on_event", tags=[f"batch_size:{size}"])
|
|
133
|
-
await asyncio.gather(*tasks)
|
|
134
|
-
log.debug(f"Completed rendering {size} jobs")
|
|
116
|
+
job.spawn()
|
|
135
117
|
finally:
|
|
136
118
|
NEW_CONTEXT.clear()
|
|
137
119
|
|
|
@@ -144,16 +126,24 @@ async def render_on_demand():
|
|
|
144
126
|
job = rendering.RenderJob(
|
|
145
127
|
id=id, request=request, context=template_context.get_context(request)
|
|
146
128
|
)
|
|
147
|
-
|
|
129
|
+
job.spawn()
|
|
148
130
|
ONDEMAND.task_done()
|
|
149
131
|
|
|
150
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
|
+
|
|
151
140
|
@asynccontextmanager
|
|
152
141
|
async def lifespan(_: FastAPI):
|
|
153
142
|
# Template Rendering
|
|
154
143
|
log.debug("Starting rendering loops")
|
|
155
144
|
asyncio.create_task(render_on_event())
|
|
156
145
|
asyncio.create_task(render_on_demand())
|
|
146
|
+
asyncio.create_task(monitor_render_queue())
|
|
157
147
|
|
|
158
148
|
# Template context
|
|
159
149
|
log.debug("Starting context loop")
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
const input = document.getElementById('filterInput');
|
|
2
|
-
const form = document.getElementById('filterForm');
|
|
3
|
-
|
|
4
|
-
window.addEventListener('DOMContentLoaded', () => {
|
|
5
|
-
const match = document.cookie.match(/(?:^|; )node_expression=([^;]*)/);
|
|
6
|
-
if (match) {
|
|
7
|
-
input.value = match[1];
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
form.addEventListener('submit', (event) => {
|
|
12
|
-
event.preventDefault();
|
|
13
|
-
const value = input.value.trim();
|
|
14
|
-
document.cookie = `node_expression=${value}; path=/ui/resources/; max-age=31536000`;
|
|
15
|
-
location.reload();
|
|
16
|
-
});
|
|
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
|
{sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/aes_gcm_cipher.py
RENAMED
|
File without changes
|
{sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/base_cipher.py
RENAMED
|
File without changes
|
{sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/disabled_cipher.py
RENAMED
|
File without changes
|
{sovereign-1.0.0b124 → sovereign-1.0.0b126}/src/sovereign/utils/crypto/suites/fernet_cipher.py
RENAMED
|
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
|