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/worker.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from contextlib import asynccontextmanager
|
|
3
|
+
from typing import final
|
|
4
|
+
|
|
5
|
+
from fastapi import Body, FastAPI
|
|
6
|
+
|
|
7
|
+
from sovereign import (
|
|
8
|
+
application_logger as log,
|
|
9
|
+
)
|
|
10
|
+
from sovereign import (
|
|
11
|
+
cache,
|
|
12
|
+
disabled_ciphersuite,
|
|
13
|
+
rendering,
|
|
14
|
+
server_cipher_container,
|
|
15
|
+
stats,
|
|
16
|
+
)
|
|
17
|
+
from sovereign.configuration import config
|
|
18
|
+
from sovereign.context import TemplateContext
|
|
19
|
+
from sovereign.events import Topic, bus
|
|
20
|
+
from sovereign.types import DiscoveryRequest, RegisterClientRequest
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# noinspection PyUnusedLocal
|
|
24
|
+
def hidden_field(*args, **kwargs):
|
|
25
|
+
return "(value hidden)"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def inject_builtin_items(request, output):
|
|
29
|
+
output["__hide_from_ui"] = lambda v: v
|
|
30
|
+
output["crypto"] = server_cipher_container
|
|
31
|
+
if request.is_internal_request:
|
|
32
|
+
output["__hide_from_ui"] = hidden_field
|
|
33
|
+
output["crypto"] = disabled_ciphersuite
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
template_context = TemplateContext.from_config()
|
|
37
|
+
context_middleware = [inject_builtin_items]
|
|
38
|
+
template_context.middleware = context_middleware
|
|
39
|
+
writer = cache.CacheWriter()
|
|
40
|
+
|
|
41
|
+
ClientId = str
|
|
42
|
+
OnDemandJob = tuple[ClientId, DiscoveryRequest]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@final
|
|
46
|
+
class RenderQueue:
|
|
47
|
+
def __init__(self, maxsize: int = 0):
|
|
48
|
+
self._queue: asyncio.Queue[OnDemandJob] = asyncio.Queue(maxsize)
|
|
49
|
+
self._set: set[ClientId] = set()
|
|
50
|
+
self._lock = asyncio.Lock()
|
|
51
|
+
|
|
52
|
+
async def put(self, item: OnDemandJob):
|
|
53
|
+
cid = item[0]
|
|
54
|
+
async with self._lock:
|
|
55
|
+
if cid not in self._set:
|
|
56
|
+
await self._queue.put(item)
|
|
57
|
+
self._set.add(cid)
|
|
58
|
+
|
|
59
|
+
def put_nowait(self, item: OnDemandJob):
|
|
60
|
+
cid = item[0]
|
|
61
|
+
if cid in self._set:
|
|
62
|
+
return
|
|
63
|
+
if self._queue.full():
|
|
64
|
+
raise asyncio.QueueFull
|
|
65
|
+
self._queue.put_nowait(item)
|
|
66
|
+
self._set.add(cid)
|
|
67
|
+
|
|
68
|
+
async def get(self):
|
|
69
|
+
return await self._queue.get()
|
|
70
|
+
|
|
71
|
+
def full(self):
|
|
72
|
+
return self._queue.full()
|
|
73
|
+
|
|
74
|
+
async def task_done(self, cid):
|
|
75
|
+
async with self._lock:
|
|
76
|
+
self._set.remove(cid)
|
|
77
|
+
self._queue.task_done()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
ONDEMAND = RenderQueue()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
poller = None
|
|
84
|
+
if config.sources is not None:
|
|
85
|
+
if config.matching is not None:
|
|
86
|
+
matching_enabled = config.matching.enabled
|
|
87
|
+
node_key: str | None = config.matching.node_key
|
|
88
|
+
source_key: str | None = config.matching.source_key
|
|
89
|
+
else:
|
|
90
|
+
matching_enabled = False
|
|
91
|
+
node_key = None
|
|
92
|
+
source_key = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def render_on_event(ctx):
|
|
96
|
+
subscription = bus.subscribe(Topic.CONTEXT)
|
|
97
|
+
while True:
|
|
98
|
+
# block forever until new context arrives
|
|
99
|
+
event = await subscription.get()
|
|
100
|
+
context_name = event.metadata.get("name")
|
|
101
|
+
|
|
102
|
+
log.debug(event.message)
|
|
103
|
+
try:
|
|
104
|
+
if registered := writer.get_registered_clients():
|
|
105
|
+
size = len(registered)
|
|
106
|
+
stats.increment("template.render_on_event", tags=[f"batch_size:{size}"])
|
|
107
|
+
|
|
108
|
+
for client, request in registered:
|
|
109
|
+
if context_name in request.template.depends_on:
|
|
110
|
+
log.info(
|
|
111
|
+
f"Rendering template on-event for {request} because {context_name} was updated"
|
|
112
|
+
)
|
|
113
|
+
job = rendering.RenderJob(
|
|
114
|
+
id=client,
|
|
115
|
+
request=request,
|
|
116
|
+
context=ctx.get_context(request),
|
|
117
|
+
)
|
|
118
|
+
job.submit()
|
|
119
|
+
|
|
120
|
+
finally:
|
|
121
|
+
await asyncio.sleep(config.template_context.cooldown)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def render_on_demand(ctx):
|
|
125
|
+
while True:
|
|
126
|
+
cid, request = await ONDEMAND.get()
|
|
127
|
+
stats.increment("template.render_on_demand")
|
|
128
|
+
log.debug(
|
|
129
|
+
f"Received on-demand request to render templates for {cid} ({request})"
|
|
130
|
+
)
|
|
131
|
+
job = rendering.RenderJob(
|
|
132
|
+
id=cid, request=request, context=ctx.get_context(request)
|
|
133
|
+
)
|
|
134
|
+
_ = job.submit()
|
|
135
|
+
await ONDEMAND.task_done(cid)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# noinspection PyProtectedMember
|
|
139
|
+
async def monitor_render_queue():
|
|
140
|
+
"""Periodically report render queue size metrics"""
|
|
141
|
+
while True:
|
|
142
|
+
await asyncio.sleep(10)
|
|
143
|
+
stats.gauge("template.on_demand_queue_size", ONDEMAND._queue.qsize())
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@asynccontextmanager
|
|
147
|
+
async def lifespan(_: FastAPI):
|
|
148
|
+
# Template Rendering
|
|
149
|
+
log.debug("Starting rendering loops")
|
|
150
|
+
asyncio.create_task(render_on_event(template_context))
|
|
151
|
+
asyncio.create_task(render_on_demand(template_context))
|
|
152
|
+
asyncio.create_task(monitor_render_queue())
|
|
153
|
+
|
|
154
|
+
# Template context
|
|
155
|
+
subscription = bus.subscribe(Topic.CONTEXT)
|
|
156
|
+
log.debug("Starting context loop")
|
|
157
|
+
asyncio.create_task(template_context.start())
|
|
158
|
+
event = await subscription.get()
|
|
159
|
+
log.debug(event.message)
|
|
160
|
+
|
|
161
|
+
log.debug("Worker lifespan initialized")
|
|
162
|
+
yield
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
worker = FastAPI(lifespan=lifespan)
|
|
166
|
+
if dsn := config.sentry_dsn.get_secret_value():
|
|
167
|
+
try:
|
|
168
|
+
# noinspection PyUnusedImports
|
|
169
|
+
import sentry_sdk
|
|
170
|
+
|
|
171
|
+
# noinspection PyUnusedImports
|
|
172
|
+
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
|
|
173
|
+
|
|
174
|
+
sentry_sdk.init(dsn)
|
|
175
|
+
worker.add_middleware(SentryAsgiMiddleware) # type: ignore
|
|
176
|
+
except ImportError: # pragma: no cover
|
|
177
|
+
log.error("Sentry DSN configured but failed to attach to worker")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@worker.get("/health")
|
|
181
|
+
def health():
|
|
182
|
+
return "OK"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@worker.put("/client")
|
|
186
|
+
async def client_add(
|
|
187
|
+
registration: RegisterClientRequest = Body(...),
|
|
188
|
+
):
|
|
189
|
+
log.info(f"Received registration: {registration.request}")
|
|
190
|
+
xds = registration.request
|
|
191
|
+
client_id, req = writer.register(xds)
|
|
192
|
+
ONDEMAND.put_nowait((client_id, req))
|
|
193
|
+
return "Registered", 200
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sovereign
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0a4
|
|
4
4
|
Summary: Envoy Proxy control-plane written in Python
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Author: Vasili Syrakis
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
Project-URL: Homepage, https://pypi.org/project/sovereign/
|
|
6
|
+
Project-URL: Repository, https://bitbucket.org/atlassian/sovereign/src/master/
|
|
7
|
+
Project-URL: Documentation, https://developer.atlassian.com/platform/sovereign/
|
|
8
|
+
Author-email: Vasili Syrakis <vsyrakis@atlassian.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Keywords: control-plane,envoy,envoyproxy,management,server
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Environment :: No Input/Output (Daemon)
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
@@ -16,83 +16,103 @@ Classifier: Intended Audience :: System Administrators
|
|
|
16
16
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
18
|
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
-
Classifier: Programming Language :: Python :: 3
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
23
|
Classifier: Topic :: Internet :: Proxy Servers
|
|
24
|
+
Requires-Python: <4.0,>=3.11.0
|
|
25
|
+
Requires-Dist: aiofiles<24,>=23.2.1
|
|
26
|
+
Requires-Dist: cachelib<0.11,>=0.10.2
|
|
27
|
+
Requires-Dist: cachetools<6,>=5.3.2
|
|
28
|
+
Requires-Dist: croniter<2,>=1.4.1
|
|
29
|
+
Requires-Dist: cryptography>=45.0.2
|
|
30
|
+
Requires-Dist: fastapi<0.129,>=0.128.0
|
|
31
|
+
Requires-Dist: glom<24,>=23.3.0
|
|
32
|
+
Requires-Dist: h11<0.17,>=0.16.0
|
|
33
|
+
Requires-Dist: jinja2<4,>=3.1.2
|
|
34
|
+
Requires-Dist: jmespath<2,>=1.0.1
|
|
35
|
+
Requires-Dist: pydantic-settings<2.6.0
|
|
36
|
+
Requires-Dist: pydantic<3,>=2.7.2
|
|
37
|
+
Requires-Dist: pyyaml<7,>=6.0.1
|
|
38
|
+
Requires-Dist: requests<3,>=2.32.4
|
|
39
|
+
Requires-Dist: rich>=14.2.0
|
|
40
|
+
Requires-Dist: starlette-context<0.4,>=0.3.6
|
|
41
|
+
Requires-Dist: starlette<0.50,>=0.49.1
|
|
42
|
+
Requires-Dist: structlog<24,>=23.1.0
|
|
43
|
+
Requires-Dist: supervisor<5,>=4.2.5
|
|
44
|
+
Requires-Dist: uvicorn<0.24,>=0.23.2
|
|
45
|
+
Requires-Dist: uvloop<1.0,>0.19.0
|
|
23
46
|
Provides-Extra: boto
|
|
47
|
+
Requires-Dist: boto3<2,>=1.28.62; extra == 'boto'
|
|
48
|
+
Provides-Extra: caching
|
|
49
|
+
Provides-Extra: httptools
|
|
50
|
+
Requires-Dist: httptools<0.7,>=0.6.0; extra == 'httptools'
|
|
24
51
|
Provides-Extra: orjson
|
|
52
|
+
Requires-Dist: orjson<4,>=3.9.15; extra == 'orjson'
|
|
25
53
|
Provides-Extra: sentry
|
|
54
|
+
Requires-Dist: sentry-sdk<3,>=2.14.0; extra == 'sentry'
|
|
26
55
|
Provides-Extra: statsd
|
|
56
|
+
Requires-Dist: datadog>=0.50.1; extra == 'statsd'
|
|
27
57
|
Provides-Extra: ujson
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist: PyYAML (>=5.4,<6.0)
|
|
30
|
-
Requires-Dist: aiofiles (>=0.8.0,<0.9.0)
|
|
31
|
-
Requires-Dist: boto3 (>=1.17.0,<2.0.0); extra == "boto"
|
|
32
|
-
Requires-Dist: cachelib (>=0.1.1,<0.2.0)
|
|
33
|
-
Requires-Dist: croniter (>=1.3.5,<2.0.0)
|
|
34
|
-
Requires-Dist: cryptography (>=38.0.3,<39.0.0)
|
|
35
|
-
Requires-Dist: datadog (>=0.39.0,<0.40.0); extra == "statsd"
|
|
36
|
-
Requires-Dist: fastapi (>=0.78.0,<0.79.0)
|
|
37
|
-
Requires-Dist: glom (>=20.11.0,<21.0.0)
|
|
38
|
-
Requires-Dist: gunicorn (>=20.1.0,<21.0.0)
|
|
39
|
-
Requires-Dist: orjson (>=3.4.7,<4.0.0); extra == "orjson"
|
|
40
|
-
Requires-Dist: requests (>=2.28.1,<3.0.0)
|
|
41
|
-
Requires-Dist: sentry-sdk (>=0.19.5,<0.20.0); extra == "sentry"
|
|
42
|
-
Requires-Dist: structlog (==21.5.0)
|
|
43
|
-
Requires-Dist: ujson (>=5.4.0,<6.0.0); extra == "ujson"
|
|
44
|
-
Requires-Dist: uvicorn (>=0.18.2,<0.19.0)
|
|
45
|
-
Requires-Dist: uvloop (>=0.16.0,<0.17.0)
|
|
46
|
-
Project-URL: Documentation, https://vsyrakis.bitbucket.io/sovereign/docs/
|
|
47
|
-
Project-URL: Repository, https://bitbucket.org/atlassian/sovereign/src/master/
|
|
58
|
+
Requires-Dist: ujson<6,>=5.8.0; extra == 'ujson'
|
|
48
59
|
Description-Content-Type: text/markdown
|
|
49
60
|
|
|
50
61
|
sovereign
|
|
51
62
|
=========
|
|
52
63
|
|
|
64
|
+
|
|
53
65
|
Mission statement
|
|
54
66
|
-----------------
|
|
55
67
|
This project implements a JSON control-plane based on the [envoy](https://envoyproxy.io) [data-plane-api](https://github.com/envoyproxy/data-plane-api)
|
|
56
68
|
|
|
57
|
-
The purpose of `sovereign` is to supply downstream envoy proxies with
|
|
58
|
-
|
|
69
|
+
The purpose of `sovereign` is to supply downstream envoy proxies with dynamic configuration.
|
|
70
|
+
|
|
59
71
|
|
|
60
72
|
Mechanism of Operation
|
|
61
73
|
----------------------
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* (optional) Applies transforms to the data
|
|
66
|
-
* Uses the data to generate Envoy configuration from templates
|
|
67
|
-
```
|
|
74
|
+
Sovereign allows you to define templates that represent each resource type
|
|
75
|
+
provided by Envoy. For example, clusters, routes, listeners, secrets,
|
|
76
|
+
extension_configs, etc.
|
|
68
77
|
|
|
69
|
-
In
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
uses the data to generate envoy configuration from either python code, or jinja2 templates.
|
|
78
|
+
In order to enrich the templates with data, Sovereign has ways of polling data
|
|
79
|
+
out-of-band which it then includes as variables that can be accessed within the
|
|
80
|
+
templates.
|
|
73
81
|
|
|
74
|
-
This
|
|
82
|
+
This allows Sovereign to provide configuration to Envoy that changes over time
|
|
83
|
+
depending on the data sources, without needing to redeploy the control-plane.
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
Sovereign provides some built-in ways of polling data (such as over HTTP, or
|
|
86
|
+
on-disk) but also exposes extension points, allowing you to write your own
|
|
87
|
+
plugins in Python.
|
|
79
88
|
|
|
80
|
-
*Modifiers* can mutate the data retrieved from sources, just in case the data
|
|
81
|
-
is in a less than favorable structure.
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
Support
|
|
91
|
+
------------
|
|
92
|
+
[Submit new issues here](https://bitbucket.org/atlassian/sovereign/issues/new)
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
If you're unable to submit an issue on Bitbucket, send an email to [vsyrakis@atlassian.com](mailto:vsyrakis@atlassian.com)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
Release
|
|
98
|
+
------------
|
|
99
|
+
See [RELEASE.md]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
Roadmap
|
|
103
|
+
------------
|
|
104
|
+
* Performance improvements
|
|
105
|
+
* Data persistence
|
|
106
|
+
* Push API (versus polling)
|
|
107
|
+
* Client for Sovereign
|
|
108
|
+
* gRPC
|
|
89
109
|
|
|
90
|
-
The JSON configuration can be viewed in real-time with Sovereign's read-only web interface.
|
|
91
110
|
|
|
92
111
|
Requirements
|
|
93
112
|
------------
|
|
94
113
|
* Python 3.8+
|
|
95
114
|
|
|
115
|
+
|
|
96
116
|
Installation
|
|
97
117
|
------------
|
|
98
118
|
```
|
|
@@ -101,62 +121,53 @@ pip install sovereign
|
|
|
101
121
|
|
|
102
122
|
Documentation
|
|
103
123
|
-------------
|
|
104
|
-
[Read the docs here!](https://
|
|
105
|
-
|
|
106
|
-
:new: Read-only user interface
|
|
107
|
-
------------------------
|
|
108
|
-
Added in `v0.5.3`!
|
|
124
|
+
[Read the docs here!](https://developer.atlassian.com/platform/sovereign/)
|
|
109
125
|
|
|
110
|
-
This interface allows you to browse the resources currently returned by Sovereign.
|
|
111
126
|
|
|
112
|
-

|
|
113
127
|
|
|
114
128
|
Local development
|
|
115
129
|
=================
|
|
116
130
|
|
|
131
|
+
|
|
117
132
|
Requirements
|
|
118
133
|
------------
|
|
134
|
+
* uv
|
|
119
135
|
* Docker
|
|
120
136
|
* Docker-compose
|
|
121
137
|
|
|
138
|
+
|
|
122
139
|
Installing dependencies for dev
|
|
123
140
|
-------------------------------
|
|
124
|
-
|
|
125
|
-
|
|
141
|
+
Dependencies and creation of virtualenv is handled by uv
|
|
126
142
|
```
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
uv sync
|
|
144
|
+
uv venv activate
|
|
145
|
+
uv run <command>
|
|
130
146
|
```
|
|
131
147
|
|
|
132
148
|
Running locally
|
|
133
149
|
---------------
|
|
134
150
|
Running the test env
|
|
135
|
-
|
|
136
151
|
```
|
|
137
152
|
make run
|
|
138
153
|
```
|
|
139
154
|
|
|
140
155
|
Running the test env daemonized
|
|
141
|
-
|
|
142
156
|
```
|
|
143
157
|
make run-daemon
|
|
144
158
|
```
|
|
145
159
|
|
|
146
160
|
Pylint
|
|
147
|
-
|
|
148
161
|
```
|
|
149
162
|
make lint
|
|
150
163
|
```
|
|
151
164
|
|
|
152
165
|
Unit tests
|
|
153
|
-
|
|
154
166
|
```
|
|
155
167
|
make unit
|
|
156
168
|
```
|
|
157
169
|
|
|
158
170
|
Acceptance tests
|
|
159
|
-
|
|
160
171
|
```
|
|
161
172
|
make run-daemon acceptance
|
|
162
173
|
```
|
|
@@ -164,7 +175,6 @@ make run-daemon acceptance
|
|
|
164
175
|
|
|
165
176
|
Contributors
|
|
166
177
|
============
|
|
167
|
-
|
|
168
178
|
Pull requests, issues and comments welcome. For pull requests:
|
|
169
179
|
|
|
170
180
|
* Add tests for new features and bug fixes
|
|
@@ -193,9 +203,7 @@ those contributing as an individual.
|
|
|
193
203
|
|
|
194
204
|
License
|
|
195
205
|
========
|
|
196
|
-
|
|
197
206
|
Copyright (c) 2018 Atlassian and others.
|
|
198
207
|
Apache 2.0 licensed, see [LICENSE.txt](LICENSE.txt) file.
|
|
199
208
|
|
|
200
209
|
|
|
201
|
-
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
sovereign/__init__.py,sha256=D6-BdAO7EyeQ7KU3sl9oiBYsGXDBkc5UM7I1kkas1Eg,1069
|
|
2
|
+
sovereign/app.py,sha256=l7AO8PtHsiVIN6ybWKj0qgzPJ-cNX8q9_syLfIG9XHY,5117
|
|
3
|
+
sovereign/configuration.py,sha256=AqDOVc7eOgEsP8BOvW5UsMhbC2j2FR72Uios76gK6Ao,21177
|
|
4
|
+
sovereign/constants.py,sha256=qdWD1lTvkaW5JGF7TmZhfksQHlRAJFVqbG7v6JQA9k8,46
|
|
5
|
+
sovereign/context.py,sha256=nwLJ_gvXFoKZJMCt4NX3KRkM381XMq_DJfTedceToJQ,9076
|
|
6
|
+
sovereign/error_info.py,sha256=_xfwYCV10zpAa0XP_zz0FoGtjj-1S_f4IgOf78-6wcw,1487
|
|
7
|
+
sovereign/events.py,sha256=gSWxaEqRdYfA_y7iFEzQPPrg3N5jy9svpfHzn6kBdRk,1203
|
|
8
|
+
sovereign/middlewares.py,sha256=6w4JpvtNGvQA4rocQsYQjuu-ckhpKT6gKYA16T-kiqA,3082
|
|
9
|
+
sovereign/rendering.py,sha256=qKtO0BUoqRs_FqDGsw2o0_756Uj59aBhfeKRpNjFlQM,4103
|
|
10
|
+
sovereign/rendering_common.py,sha256=WvZ_vCHD3ODiAX3Hw6Q6GwTklH4a2BFY3-zwqDNnmjg,3395
|
|
11
|
+
sovereign/response_class.py,sha256=S-8xziWdWVk8jbde_2OdxV1XWdrw6EwInLLhFmaYgkA,427
|
|
12
|
+
sovereign/server.py,sha256=5JQJ72yyOA4rjVs4rFznwYvh7zFEsg48cTDBqCvmsQo,3650
|
|
13
|
+
sovereign/statistics.py,sha256=0bbL7eLGOC-_hOY_ilx2uV_VpSJTIYjBGvEztDlgQDs,2045
|
|
14
|
+
sovereign/tracing.py,sha256=xL1blm4u2gmahpkFKhwY-pA-hE976rDXi9QxaOdRnoI,2686
|
|
15
|
+
sovereign/types.py,sha256=j8w3tXfkm0FHcMP8WXcXd99D1YhJ-cIuc-oVV0LuBSc,10245
|
|
16
|
+
sovereign/worker.py,sha256=ecLm_LDnbJPVzQPLmBhF3J-cWpzPKo6H2HAncDNbneE,5615
|
|
17
|
+
sovereign/cache/__init__.py,sha256=bq5BPiO-oCpSmiQ7GoXDk3PckVqYNS2NADguWBcRzeQ,9536
|
|
18
|
+
sovereign/cache/filesystem.py,sha256=GQP34jhJP2JsaEQgnI4O80EuN6sdK7lnKbP37jrI4d8,2416
|
|
19
|
+
sovereign/cache/types.py,sha256=JOLrPI8X209Fndt5xtLtEWMB6Fh7cvnzwsEBAoBV06c,243
|
|
20
|
+
sovereign/cache/backends/__init__.py,sha256=3oKlNWuHd5KyETor1napzq6nIJv0KGbOEPUrIZf35So,3160
|
|
21
|
+
sovereign/cache/backends/s3.py,sha256=92YPCBW9t4wLNQuCiES29GJ36lUP1IWIJDMHWFGNJDA,5865
|
|
22
|
+
sovereign/dynamic_config/__init__.py,sha256=VU71zUxc6zwioX3zm9VTzj_XQl8ye6FJGPuna3BaieY,3546
|
|
23
|
+
sovereign/dynamic_config/deser.py,sha256=uJsv6vWEPb-dJ9PIDZ51J-KC8J-IikbTvUM8hmvKAyY,1834
|
|
24
|
+
sovereign/dynamic_config/loaders.py,sha256=hp8drF4jXaotO-7CGudgs_enkg63vrXW34nTzP4MHlY,2915
|
|
25
|
+
sovereign/logging/access_logger.py,sha256=W5GE0fOjkYTwdA8KVsbPzof_FcPfJ8kCOpnL_tEi9gw,2989
|
|
26
|
+
sovereign/logging/application_logger.py,sha256=DKrLVX8sl7ckGYCUlSJLwJ9K9nomNoLBitzhEj2s_K8,1806
|
|
27
|
+
sovereign/logging/base_logger.py,sha256=ScOzHs8Rt1RZaUZGvaJSAlDEjD0BxkD5sLKSm2GgM0I,1243
|
|
28
|
+
sovereign/logging/bootstrapper.py,sha256=9a13x8gDEH0fiDkpEkwxmQYzWmb8jqPJKx4QCgdT7II,1302
|
|
29
|
+
sovereign/logging/types.py,sha256=rGqJAEVvgvzHy4aPfvEH6yQ-yblXNkEcWG7G8l9ALEA,282
|
|
30
|
+
sovereign/modifiers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
sovereign/modifiers/lib.py,sha256=tck5rw6PNqLofBtm3GTRMmrfrV9eSj_Rwhn5tM3Q2UA,2885
|
|
32
|
+
sovereign/templates/base.html,sha256=MMhhvvClTixKibYfhXm8Ezx6ttu6Sqki44niciCPMO4,2990
|
|
33
|
+
sovereign/templates/err.html,sha256=a3cEzOqyqWOIe3YxfTEjkxbTfxBxq1knD6GwzEFljfs,603
|
|
34
|
+
sovereign/templates/resources.html,sha256=5MfXHW8s3tAWda66Q48zVgDhZNLwHGsdCKkKHLZohIs,10420
|
|
35
|
+
sovereign/testing/loaders.py,sha256=JEjoxPSy0O-SUowW1U7P_BX6ABuqrmUrCuAxsUZaMos,200
|
|
36
|
+
sovereign/testing/modifiers.py,sha256=YI0aJOEbb1C_1T5VDrZKyLopk8JiIlKtUtxXMFfsCtk,290
|
|
37
|
+
sovereign/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
sovereign/utils/auth.py,sha256=e9lMNo8KqScTBITz4upQ0qL8A-4DTl-ybCy9DPwpfoM,2307
|
|
39
|
+
sovereign/utils/dictupdate.py,sha256=R60mHb3qf4r-KT-SyxQJv7KQktmtzp-bRtVOjebo0To,2558
|
|
40
|
+
sovereign/utils/eds.py,sha256=eykFVKaLXoMeieJ2TnArx3q5KIJeQYiB-OYQrTWtslw,4391
|
|
41
|
+
sovereign/utils/entry_point_loader.py,sha256=OPvaLfwUH0XGYw9WM3hdi_-uBAwmibBoq2NEjLFtnKs,526
|
|
42
|
+
sovereign/utils/mock.py,sha256=Oc5p8conSku2t6b75m7nMHsESepLmnv_itHcAE8cIXw,2396
|
|
43
|
+
sovereign/utils/resources.py,sha256=otKaJZIDQa4So_cKkFN9gBsLZaG6rH0hg9hjELn1Qic,472
|
|
44
|
+
sovereign/utils/templates.py,sha256=yML5rU-BUY-2oQx6gOK7fOkLjtD5VSmaH8vzPKnetSc,1059
|
|
45
|
+
sovereign/utils/timer.py,sha256=DvbjDOSXZsGXigWpuRxE1NyVrpq4qktIRQhJIOt2Dcs,919
|
|
46
|
+
sovereign/utils/version_info.py,sha256=adBfu0z6jsg8E5-BIUjZyBwZvfLASj7fpCpYeIvBeMY,576
|
|
47
|
+
sovereign/utils/weighted_clusters.py,sha256=RTRLX5TFvL426_B-M8XhC7FeGelHzpgGpHwQaVFEHRQ,1167
|
|
48
|
+
sovereign/utils/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
+
sovereign/utils/crypto/crypto.py,sha256=FMo4TikqWcPJ5fyc6j2WEHkkwZU3WY3qJKr7VLfckv0,4532
|
|
50
|
+
sovereign/utils/crypto/suites/__init__.py,sha256=smMvNa1VsQ0PvsNj6lnRNh4ktB7dMnas1CqeTOFqgGA,526
|
|
51
|
+
sovereign/utils/crypto/suites/aes_gcm_cipher.py,sha256=Yjfj1LCQDGTzHBjrZR3-koh29L_N34v65kPoIfta0aw,1239
|
|
52
|
+
sovereign/utils/crypto/suites/base_cipher.py,sha256=kUOZh_ZIILyo5zv99-qzbJZDpeMmt76vhkBDEPvAt4A,454
|
|
53
|
+
sovereign/utils/crypto/suites/disabled_cipher.py,sha256=3uagd2IkE-IHLkfOgX1rofatHIBVLryV_hGvdJPmuTM,579
|
|
54
|
+
sovereign/utils/crypto/suites/fernet_cipher.py,sha256=rP6M5ys1vctyadOxDGNFoyerWPUOunLQdZ2jjS1pxzc,701
|
|
55
|
+
sovereign/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
+
sovereign/v2/logging.py,sha256=ia8ntWngTKXlMzFhkVNScIDaFTrL0HkIkUT-afQGEJE,2639
|
|
57
|
+
sovereign/v2/types.py,sha256=0qclM0aylR1-FjCVCzlYlZShNICWt6wI-52B3SkSC1w,800
|
|
58
|
+
sovereign/v2/web.py,sha256=dzjLJ0lXdGZqkGc237QHVe930Hmls9m_e-clas9mOps,3443
|
|
59
|
+
sovereign/v2/worker.py,sha256=rGqDsinDCtGNbCmBDF1mHIik4KJH0rKpH4WLzL-VDAo,7467
|
|
60
|
+
sovereign/v2/data/data_store.py,sha256=IDjV3ouYjVxWdcvcAUIPrfnXVwAbbyMmkbXjdfKfnw8,20310
|
|
61
|
+
sovereign/v2/data/render_discovery_response.py,sha256=3AFMXBu1aBRaSLjUTxVUI9Lt-to0B5eXY5m1SMO7dB8,949
|
|
62
|
+
sovereign/v2/data/repositories.py,sha256=KsdFvOIDZmkZDe-SmatX4GialhRk4LWD_2pOBeOTAQs,3292
|
|
63
|
+
sovereign/v2/data/utils.py,sha256=cJ93ioHXPHquMhqvYY4SulbWVgTPxY6t_gvYhOt6T1s,1075
|
|
64
|
+
sovereign/v2/data/worker_queue.py,sha256=aDTLqeXM-qx-CJ0VpOaNVKkGmlEllVQxERXIaS0Kv9w,10407
|
|
65
|
+
sovereign/v2/jobs/refresh_context.py,sha256=lhzP9zl8U4fA0ND-SFJs4HefnEAtv8d7no4u5cFI-L4,4277
|
|
66
|
+
sovereign/v2/jobs/render_discovery_job.py,sha256=Z5L4Qj54tg8FVlaMaBBCZiJgAqJIS-Rl28ekxoJ1o58,5451
|
|
67
|
+
sovereign/views/__init__.py,sha256=0niB6LnhAZQzI4nk_xXsWk95Idm0YFINLoL8WhNdA70,183
|
|
68
|
+
sovereign/views/api.py,sha256=dcNy9HKEB9n6DAQI_gP5cyVLn_4YFOODMx1zIu2-J6Y,2954
|
|
69
|
+
sovereign/views/crypto.py,sha256=0UX0mEGQmiwRcR8gMeIkNYY2ykEGpPC09xmfFRsh-bY,3366
|
|
70
|
+
sovereign/views/discovery.py,sha256=1yIKSIuVnPT7wtPd0fJ7KdEp5ZZf8PdWP67XoI_4R6s,3111
|
|
71
|
+
sovereign/views/healthchecks.py,sha256=56exKqRz4Qdz3KJlbwHWZNIQ-yJUOgpDnzROrzQn3rg,4108
|
|
72
|
+
sovereign/views/interface.py,sha256=vHTdboqhBMGyX4COnExKbal51hOXFX4txaK0nmz8bs8,9294
|
|
73
|
+
sovereign_files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
74
|
+
sovereign_files/static/darkmode.js,sha256=3ip-eKGctDvNhN7UgmaHhzls7r5qIY-Jvh2EpefHbQ0,1449
|
|
75
|
+
sovereign_files/static/node_expression.js,sha256=GKxKTSRc_96IbL3H4L_31ueJFXq4N7scm5R1RNqxP24,1489
|
|
76
|
+
sovereign_files/static/panel.js,sha256=i5mGExjv-I4Gtt9dQiTyFwPZa8pg5rXeuTeidXNUiTE,2695
|
|
77
|
+
sovereign_files/static/resources.css,sha256=Rt_ir_FkoI-VIAOqPhk0vILy8kB2egAYbQU26SOs1io,4500
|
|
78
|
+
sovereign_files/static/resources.js,sha256=-TaXZ6tohyKA1SkX5YwrTcV5M8mOZ68cvEXpvZWznTo,24506
|
|
79
|
+
sovereign_files/static/style.css,sha256=kmvkJ2820RKehWxhddkucbgFkvnpUgBMteOtpEuXjvQ,601347
|
|
80
|
+
sovereign_files/static/style.css.map,sha256=h1ufjfDVX-8z-FuJqFG2-U9AVdi66U-e8uyiGdUZjDw,66576
|
|
81
|
+
sovereign_files/static/sass/style.scss,sha256=LdGXXuHi_tyMc7XhijIOrlIxyfLt827AAs2Z7DYpFpg,990
|
|
82
|
+
sovereign-1.0.0a4.dist-info/METADATA,sha256=i7YfqN62JPqLC2-55IdBfaXkhZaajFwufE6XG6glcG0,5943
|
|
83
|
+
sovereign-1.0.0a4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
84
|
+
sovereign-1.0.0a4.dist-info/entry_points.txt,sha256=Mj1IvDg3Y61lasbqkGtEWpPn_xgYWCyeuMnOl8hTLVQ,1787
|
|
85
|
+
sovereign-1.0.0a4.dist-info/RECORD,,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
sovereign = sovereign.server:main
|
|
3
|
+
sovereign-web = sovereign.server:web
|
|
4
|
+
sovereign-worker = sovereign.server:worker
|
|
5
|
+
|
|
6
|
+
[sovereign.cache.backends]
|
|
7
|
+
s3 = sovereign.cache.backends.s3:S3Backend
|
|
8
|
+
|
|
9
|
+
[sovereign.data_stores]
|
|
10
|
+
memory = sovereign.v2.data.data_store:InMemoryDataStore
|
|
11
|
+
sqlite = sovereign.v2.data.data_store:SqliteDataStore
|
|
12
|
+
|
|
13
|
+
[sovereign.deserializers]
|
|
14
|
+
jinja = sovereign.dynamic_config.deser:JinjaDeserializer
|
|
15
|
+
jinja2 = sovereign.dynamic_config.deser:JinjaDeserializer
|
|
16
|
+
json = sovereign.dynamic_config.deser:JsonDeserializer
|
|
17
|
+
none = sovereign.dynamic_config.deser:PassthroughDeserializer
|
|
18
|
+
orjson = sovereign.dynamic_config.deser:OrjsonDeserializer
|
|
19
|
+
passthrough = sovereign.dynamic_config.deser:PassthroughDeserializer
|
|
20
|
+
raw = sovereign.dynamic_config.deser:PassthroughDeserializer
|
|
21
|
+
string = sovereign.dynamic_config.deser:StringDeserializer
|
|
22
|
+
ujson = sovereign.dynamic_config.deser:UjsonDeserializer
|
|
23
|
+
yaml = sovereign.dynamic_config.deser:YamlDeserializer
|
|
24
|
+
|
|
25
|
+
[sovereign.loaders]
|
|
26
|
+
env = sovereign.dynamic_config.loaders:EnvironmentVariable
|
|
27
|
+
example = sovereign.testing.loaders:Multiply
|
|
28
|
+
file = sovereign.dynamic_config.loaders:File
|
|
29
|
+
http = sovereign.dynamic_config.loaders:Web
|
|
30
|
+
https = sovereign.dynamic_config.loaders:Web
|
|
31
|
+
inline = sovereign.dynamic_config.loaders:Inline
|
|
32
|
+
module = sovereign.dynamic_config.loaders:PythonModule
|
|
33
|
+
pkgdata = sovereign.dynamic_config.loaders:PackageData
|
|
34
|
+
python = sovereign.dynamic_config.loaders:PythonInlineCode
|
|
35
|
+
s3 = sovereign.dynamic_config.loaders:S3Bucket
|
|
36
|
+
|
|
37
|
+
[sovereign.modifiers]
|
|
38
|
+
sovereign_3rd_party_test = sovereign.testing.modifiers:Test
|
|
39
|
+
|
|
40
|
+
[sovereign.queues]
|
|
41
|
+
memory = sovereign.v2.data.worker_queue:InMemoryQueue
|
|
42
|
+
sqlite = sovereign.v2.data.worker_queue:SqliteQueue
|
|
43
|
+
|
|
44
|
+
[sovereign.sources]
|
|
45
|
+
file = sovereign.sources.file:File
|
|
46
|
+
inline = sovereign.sources.inline:Inline
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
2
|
+
const darkmode = "theme-dark";
|
|
3
|
+
const lightmode = "theme-light";
|
|
4
|
+
const toggle = document.getElementById('dark-mode-toggle');
|
|
5
|
+
const htmlTag = document.documentElement;
|
|
6
|
+
|
|
7
|
+
function preferredTheme() {
|
|
8
|
+
const preference = localStorage.getItem("theme");
|
|
9
|
+
if (preference) {
|
|
10
|
+
return preference;
|
|
11
|
+
}
|
|
12
|
+
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
13
|
+
return "dark";
|
|
14
|
+
} else {
|
|
15
|
+
return "light";
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function currentTheme() {
|
|
20
|
+
if (htmlTag.classList.contains(darkmode)) {
|
|
21
|
+
return "dark"
|
|
22
|
+
} else {
|
|
23
|
+
return "light"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function setTheme(theme) {
|
|
28
|
+
localStorage.setItem("theme", theme);
|
|
29
|
+
if (theme === "dark") {
|
|
30
|
+
htmlTag.classList.remove(lightmode);
|
|
31
|
+
htmlTag.classList.add(darkmode);
|
|
32
|
+
toggle.textContent = '🌘';
|
|
33
|
+
} else {
|
|
34
|
+
htmlTag.classList.remove(darkmode);
|
|
35
|
+
htmlTag.classList.add(lightmode);
|
|
36
|
+
toggle.textContent = '🌞';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setTheme(preferredTheme());
|
|
41
|
+
|
|
42
|
+
toggle.addEventListener("click", function() {
|
|
43
|
+
let current = currentTheme();
|
|
44
|
+
console.log("Current theme: " + current);
|
|
45
|
+
if (current === "dark") {
|
|
46
|
+
setTheme("light");
|
|
47
|
+
} else {
|
|
48
|
+
setTheme("dark");
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|