sovereign 0.19.3__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.

Files changed (99) hide show
  1. sovereign/__init__.py +13 -81
  2. sovereign/app.py +62 -48
  3. sovereign/cache/__init__.py +245 -0
  4. sovereign/cache/backends/__init__.py +110 -0
  5. sovereign/cache/backends/s3.py +161 -0
  6. sovereign/cache/filesystem.py +74 -0
  7. sovereign/cache/types.py +17 -0
  8. sovereign/configuration.py +607 -0
  9. sovereign/constants.py +1 -0
  10. sovereign/context.py +270 -104
  11. sovereign/dynamic_config/__init__.py +112 -0
  12. sovereign/dynamic_config/deser.py +78 -0
  13. sovereign/dynamic_config/loaders.py +120 -0
  14. sovereign/error_info.py +2 -3
  15. sovereign/events.py +49 -0
  16. sovereign/logging/access_logger.py +85 -0
  17. sovereign/logging/application_logger.py +54 -0
  18. sovereign/logging/base_logger.py +41 -0
  19. sovereign/logging/bootstrapper.py +36 -0
  20. sovereign/logging/types.py +10 -0
  21. sovereign/middlewares.py +8 -7
  22. sovereign/modifiers/lib.py +2 -1
  23. sovereign/rendering.py +124 -0
  24. sovereign/rendering_common.py +91 -0
  25. sovereign/response_class.py +18 -0
  26. sovereign/server.py +112 -35
  27. sovereign/statistics.py +19 -21
  28. sovereign/templates/base.html +59 -46
  29. sovereign/templates/resources.html +203 -102
  30. sovereign/testing/loaders.py +9 -0
  31. sovereign/{modifiers/test.py → testing/modifiers.py} +0 -2
  32. sovereign/tracing.py +103 -0
  33. sovereign/types.py +304 -0
  34. sovereign/utils/auth.py +27 -13
  35. sovereign/utils/crypto/__init__.py +0 -0
  36. sovereign/utils/crypto/crypto.py +135 -0
  37. sovereign/utils/crypto/suites/__init__.py +21 -0
  38. sovereign/utils/crypto/suites/aes_gcm_cipher.py +42 -0
  39. sovereign/utils/crypto/suites/base_cipher.py +21 -0
  40. sovereign/utils/crypto/suites/disabled_cipher.py +25 -0
  41. sovereign/utils/crypto/suites/fernet_cipher.py +29 -0
  42. sovereign/utils/dictupdate.py +3 -2
  43. sovereign/utils/eds.py +40 -22
  44. sovereign/utils/entry_point_loader.py +2 -2
  45. sovereign/utils/mock.py +56 -17
  46. sovereign/utils/resources.py +17 -0
  47. sovereign/utils/templates.py +4 -2
  48. sovereign/utils/timer.py +5 -3
  49. sovereign/utils/version_info.py +8 -0
  50. sovereign/utils/weighted_clusters.py +2 -1
  51. sovereign/v2/__init__.py +0 -0
  52. sovereign/v2/data/data_store.py +621 -0
  53. sovereign/v2/data/render_discovery_response.py +24 -0
  54. sovereign/v2/data/repositories.py +90 -0
  55. sovereign/v2/data/utils.py +33 -0
  56. sovereign/v2/data/worker_queue.py +273 -0
  57. sovereign/v2/jobs/refresh_context.py +117 -0
  58. sovereign/v2/jobs/render_discovery_job.py +145 -0
  59. sovereign/v2/logging.py +81 -0
  60. sovereign/v2/types.py +41 -0
  61. sovereign/v2/web.py +101 -0
  62. sovereign/v2/worker.py +199 -0
  63. sovereign/views/__init__.py +7 -0
  64. sovereign/views/api.py +82 -0
  65. sovereign/views/crypto.py +46 -15
  66. sovereign/views/discovery.py +55 -119
  67. sovereign/views/healthchecks.py +107 -20
  68. sovereign/views/interface.py +171 -111
  69. sovereign/worker.py +193 -0
  70. {sovereign-0.19.3.dist-info → sovereign-1.0.0a4.dist-info}/METADATA +80 -76
  71. sovereign-1.0.0a4.dist-info/RECORD +85 -0
  72. {sovereign-0.19.3.dist-info → sovereign-1.0.0a4.dist-info}/WHEEL +1 -1
  73. sovereign-1.0.0a4.dist-info/entry_points.txt +46 -0
  74. sovereign_files/__init__.py +0 -0
  75. sovereign_files/static/darkmode.js +51 -0
  76. sovereign_files/static/node_expression.js +42 -0
  77. sovereign_files/static/panel.js +76 -0
  78. sovereign_files/static/resources.css +246 -0
  79. sovereign_files/static/resources.js +642 -0
  80. sovereign_files/static/sass/style.scss +33 -0
  81. sovereign_files/static/style.css +16143 -0
  82. sovereign_files/static/style.css.map +1 -0
  83. sovereign/config_loader.py +0 -225
  84. sovereign/discovery.py +0 -175
  85. sovereign/logs.py +0 -131
  86. sovereign/schemas.py +0 -780
  87. sovereign/sources/__init__.py +0 -3
  88. sovereign/sources/file.py +0 -21
  89. sovereign/sources/inline.py +0 -38
  90. sovereign/sources/lib.py +0 -40
  91. sovereign/sources/poller.py +0 -294
  92. sovereign/static/sass/style.scss +0 -27
  93. sovereign/static/style.css +0 -13553
  94. sovereign/templates/ul_filter.html +0 -22
  95. sovereign/utils/crypto.py +0 -103
  96. sovereign/views/admin.py +0 -120
  97. sovereign-0.19.3.dist-info/LICENSE.txt +0 -13
  98. sovereign-0.19.3.dist-info/RECORD +0 -47
  99. sovereign-0.19.3.dist-info/entry_points.txt +0 -10
@@ -0,0 +1,18 @@
1
+ from importlib.util import find_spec
2
+ from typing import Type
3
+
4
+ from fastapi.responses import JSONResponse
5
+
6
+ json_response_class: Type[JSONResponse] = JSONResponse
7
+ if find_spec("orjson"):
8
+ from fastapi.responses import ORJSONResponse
9
+
10
+ json_response_class = ORJSONResponse
11
+
12
+ elif find_spec("ujson"):
13
+ from fastapi.responses import UJSONResponse
14
+
15
+ json_response_class = UJSONResponse
16
+
17
+
18
+ __all__ = ["json_response_class"]
sovereign/server.py CHANGED
@@ -1,49 +1,126 @@
1
- import gunicorn.app.base
2
- from fastapi import FastAPI
3
- from typing import Optional, Dict, Any, Callable
4
- from sovereign import asgi_config
5
- from sovereign.app import app
6
- from sovereign.utils.entry_point_loader import EntryPointLoader
1
+ import configparser
2
+ import tempfile
3
+ import warnings
4
+ from pathlib import Path
7
5
 
6
+ import uvicorn
8
7
 
9
- class StandaloneApplication(gunicorn.app.base.BaseApplication): # type: ignore
10
- _HOOKS = ["pre_fork", "post_fork"]
8
+ from sovereign import application_logger as log
9
+ from sovereign.configuration import SovereignAsgiConfig, SupervisordConfig, config
10
+ from sovereign.v2.worker import Worker
11
11
 
12
- def __init__(
13
- self, application: FastAPI, options: Optional[Dict[str, Any]] = None
14
- ) -> None:
15
- self.loader = EntryPointLoader(*self._HOOKS)
16
- self.options = options or {}
17
- self.application = application
18
- super().__init__()
12
+ # noinspection PyArgumentList
13
+ asgi_config = SovereignAsgiConfig()
14
+ # noinspection PyArgumentList
15
+ supervisord_config = SupervisordConfig()
19
16
 
20
- def load_config(self) -> None:
21
- for key, value in self.options.items():
22
- self.cfg.set(key.lower(), value)
23
17
 
24
- for hook in self._HOOKS:
25
- self._install_hooks(hook)
18
+ def web(supervisor_enabled=True) -> None:
19
+ from sovereign.app import app
26
20
 
27
- def _install_hooks(self, name: str) -> None:
28
- hooks: list[Callable[[Any, Any], None]] = [
29
- ep.load() for ep in self.loader.groups[name]
30
- ]
21
+ log.debug("Starting web server")
31
22
 
32
- def master_hook(server: Any, worker: Any) -> None:
33
- for hook in hooks:
34
- hook(server, worker)
23
+ if not supervisor_enabled:
24
+ uvicorn.run(
25
+ app,
26
+ log_level=asgi_config.log_level,
27
+ access_log=False,
28
+ timeout_keep_alive=asgi_config.keepalive,
29
+ host=asgi_config.host,
30
+ port=asgi_config.port,
31
+ workers=1, # per managed supervisor proc
32
+ )
33
+ else:
34
+ uvicorn.run(
35
+ app,
36
+ fd=0,
37
+ log_level=asgi_config.log_level,
38
+ access_log=False,
39
+ timeout_keep_alive=asgi_config.keepalive,
40
+ host=asgi_config.host,
41
+ port=asgi_config.port,
42
+ workers=1, # per managed supervisor proc
43
+ )
35
44
 
36
- self.cfg.set(name, master_hook)
37
45
 
38
- def load(self) -> FastAPI:
39
- return self.application
46
+ def worker():
47
+ if config.worker_v2_enabled:
48
+ log.debug("Starting worker v2")
49
+ Worker().start()
50
+ else:
51
+ from sovereign.worker import worker as worker_app
40
52
 
53
+ log.debug("Starting worker")
54
+ uvicorn.run(
55
+ worker_app,
56
+ log_level=asgi_config.log_level,
57
+ access_log=False,
58
+ timeout_keep_alive=asgi_config.keepalive,
59
+ host="127.0.0.1",
60
+ port=9080,
61
+ workers=1, # per managed supervisor proc
62
+ )
41
63
 
42
- def main() -> None:
43
- asgi = StandaloneApplication(
44
- application=app, options=asgi_config.as_gunicorn_conf()
45
- )
46
- asgi.run()
64
+
65
+ def write_supervisor_conf() -> Path:
66
+ proc_env = {
67
+ "LANG": "en_US.UTF-8",
68
+ "LC_ALL": "en_US.UTF-8",
69
+ }
70
+ base = {
71
+ "autostart": "true",
72
+ "autorestart": "true",
73
+ "stdout_logfile": "/dev/stdout",
74
+ "stdout_logfile_maxbytes": "0",
75
+ "stderr_logfile": "/dev/stderr",
76
+ "stderr_logfile_maxbytes": "0",
77
+ "stopsignal": "QUIT",
78
+ "environment": ",".join(["=".join((k, v)) for k, v in proc_env.items()]),
79
+ }
80
+
81
+ conf = configparser.RawConfigParser()
82
+ conf["supervisord"] = supervisord = {
83
+ "nodaemon": str(supervisord_config.nodaemon).lower(),
84
+ "loglevel": supervisord_config.loglevel,
85
+ "pidfile": supervisord_config.pidfile,
86
+ "logfile": supervisord_config.logfile,
87
+ "directory": supervisord_config.directory,
88
+ }
89
+
90
+ conf["fcgi-program:web"] = web = {
91
+ **base,
92
+ "socket": f"tcp://{asgi_config.host}:{asgi_config.port}",
93
+ "numprocs": str(asgi_config.workers),
94
+ "process_name": "%(program_name)s-%(process_num)02d",
95
+ "command": "sovereign-web", # default niceness, higher CPU priority
96
+ }
97
+
98
+ conf["program:data"] = worker = {
99
+ **base,
100
+ "numprocs": "1",
101
+ "command": "nice -n 2 sovereign-worker", # run worker with reduced CPU priority (higher niceness value)
102
+ }
103
+
104
+ if user := asgi_config.user:
105
+ supervisord["user"] = user
106
+ web["user"] = user
107
+ worker["user"] = user
108
+
109
+ log.debug("Writing supervisor config")
110
+ with tempfile.NamedTemporaryFile("w", delete=False) as f:
111
+ conf.write(f)
112
+ log.debug("Supervisor config written out")
113
+ return Path(f.name)
114
+
115
+
116
+ def main():
117
+ path = write_supervisor_conf()
118
+ with warnings.catch_warnings():
119
+ warnings.simplefilter("ignore")
120
+ from supervisor import supervisord
121
+
122
+ log.debug("Starting processes")
123
+ supervisord.main(["-c", path])
47
124
 
48
125
 
49
126
  if __name__ == "__main__":
sovereign/statistics.py CHANGED
@@ -1,15 +1,15 @@
1
1
  import logging
2
- from typing import Optional, Any, Callable, Dict
3
2
  from functools import wraps
4
- from sovereign.schemas import StatsdConfig
3
+ from typing import Any, Callable, Dict, Optional
5
4
 
6
- emitted: Dict[str, Any] = dict()
5
+ from sovereign.configuration import config as sovereign_config
6
+
7
+ STATSD: Dict[str, Optional["StatsDProxy"]] = {"instance": None}
7
8
 
8
9
 
9
10
  class StatsDProxy:
10
11
  def __init__(self, statsd_instance: Optional[Any] = None) -> None:
11
12
  self.statsd = statsd_instance
12
- self.emitted = emitted
13
13
 
14
14
  def __getattr__(self, item: str) -> Any:
15
15
  if self.statsd is not None:
@@ -20,14 +20,12 @@ class StatsDProxy:
20
20
  return self.do_nothing
21
21
 
22
22
  def do_nothing(self, *args: Any, **kwargs: Any) -> None:
23
- k = args[0]
24
- emitted[k] = emitted.setdefault(k, 0) + 1
23
+ _ = args[0]
25
24
 
26
25
 
27
26
  class StatsdNoop:
28
- def __init__(self, *args: Any, **kwargs: Any) -> None:
29
- k = args[0]
30
- emitted[k] = emitted.setdefault(k, 0) + 1
27
+ def __init__(self, *args, **kwargs):
28
+ pass
31
29
 
32
30
  def __enter__(self): # type: ignore
33
31
  return self
@@ -43,21 +41,18 @@ class StatsdNoop:
43
41
  return wrapped
44
42
 
45
43
 
46
- def configure_statsd(config: StatsdConfig) -> StatsDProxy:
44
+ def configure_statsd() -> StatsDProxy:
45
+ if STATSD["instance"] is not None:
46
+ return STATSD["instance"]
47
+ config = sovereign_config.statsd
47
48
  try:
48
49
  from datadog import DogStatsd
49
50
 
50
- class CustomStatsd(DogStatsd): # type: ignore
51
- def _report(self, metric, metric_type, value, tags, sample_rate) -> None: # type: ignore
52
- super()._report(metric, metric_type, value, tags, sample_rate)
53
- self.emitted: Dict[str, Any] = dict()
54
- self.emitted[metric] = self.emitted.setdefault(metric, 0) + 1
55
-
56
- module: Optional[CustomStatsd]
57
- module = CustomStatsd()
58
- if config.enabled:
51
+ module: Optional[DogStatsd]
52
+ module = DogStatsd()
53
+ if config.enabled and module:
59
54
  module.host = config.host
60
- module.port = config.port
55
+ module.port = int(config.port)
61
56
  module.namespace = config.namespace
62
57
  module.use_ms = config.use_ms
63
58
  for tag, value in config.tags.items():
@@ -71,4 +66,7 @@ def configure_statsd(config: StatsdConfig) -> StatsDProxy:
71
66
  raise
72
67
  module = None
73
68
 
74
- return StatsDProxy(module)
69
+ ret = StatsDProxy(module)
70
+ if STATSD["instance"] is None:
71
+ STATSD["instance"] = ret
72
+ return ret
@@ -1,64 +1,77 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en" class="has-navbar-fixed-top">
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
7
  <link rel="stylesheet" type="text/css" href="/static/style.css">
8
8
  <title>{% block title %}{% endblock %} - Sovereign</title>
9
+ <script src="/static/darkmode.js"></script>
9
10
  {%- block head %}{% endblock %}
10
11
  </head>
11
12
  <body>
12
- <div class="columns">
13
- <div class="column"></div>
14
13
  {%- block nav %}
15
- <div class="column is-two-thirds">
16
- {%- set active_page = resource_type|default('redirect_to_docs') -%}
17
- <nav class="navbar is-primary is-fixed-top" role="navigation" aria-label="main navigation">
18
- <div class="navbar-brand">
19
- <a class="navbar-item" href="/ui">
20
- <h4 class="title is-4" style="color: white">
21
- sovereign
22
- </h4>
23
- </a>
24
- </div>
25
- <div id="navbar" class="navbar-menu">
26
- <div class="navbar-start">
27
- <div class="navbar-item has-dropdown is-hoverable">
28
- <a class="navbar-link">
29
- Resources
30
- </a>
31
- <div class="navbar-dropdown">
32
- {%- for type in all_types %}
33
- <a class="navbar-item{% if type == active_page %} is-active{% endif %}"
34
- href="/ui/resources/{{ type }}">
35
- {{ type }}
14
+ <div class="hero">
15
+ <div class="hero-body p-4">
16
+ <a href="#">
17
+ <h3 class="title is-3" style="margin-bottom: 0px">sovereign</h3>
18
+ <h7 class="title is-7">version {{ sovereign_version }}</h7>
19
+ </a>
20
+ </div>
21
+ </div>
22
+
23
+ <div class="columns is-gapless" style="min-height: calc(100vh - 120px);">
24
+ <div class="column is-narrow" style="width: 250px;">
25
+ <aside class="menu p-4" style="height: 100%;">
26
+ <div class="mb-4">
27
+ <p class="menu-label">RESOURCES</p>
28
+ <ul class="menu-list">
29
+ {%- set active_page = resource_type|default('redirect_to_docs') -%}
30
+ {%- for type in all_types %}
31
+ <li style="padding: 2px">
32
+ <a href="/ui/resources/{{ type }}"
33
+ style="padding: 3px; border-radius: 4px"
34
+ {% if type == active_page %}class="is-active has-background-primary has-text-white"{% endif %}>
35
+ {{ type|capitalize }}
36
36
  </a>
37
- {%- endfor %}
38
- </div>
39
- </div>
37
+ </li>
38
+ {%- endfor %}
39
+ </ul>
40
40
  </div>
41
- <div class="navbar-end">
42
- <div class="navbar-item">
43
- <a class="button is-dark" href="/docs">
44
- <span>OpenAPI</span>
45
- </a>
46
- </div>
41
+
42
+ <div class="mb-4">
43
+ <p class="menu-label">LINKS</p>
44
+ <ul class="menu-list">
45
+ <li>
46
+ <a href="/docs">OpenAPI Spec</a>
47
+ </li>
48
+ <li>
49
+ <a href="https://developer.atlassian.com/platform/sovereign/">Documentation</a>
50
+ </li>
51
+ <li>
52
+ <a href="https://bitbucket.org/atlassian/sovereign">Repository</a>
53
+ </li>
54
+ </ul>
47
55
  </div>
56
+
57
+ <p class="menu-label">THEME</p>
58
+ <button id="dark-mode-toggle" class="button is-small">
59
+ <span>🌘</span>
60
+ </button>
61
+ </aside>
62
+ </div>
63
+
64
+ <div class="column">
65
+ <div class="p-4" style="max-width: 1000px">
66
+ {%- endblock %}
67
+ {%- block body %}
68
+ {% endblock -%}
48
69
  </div>
49
- </nav>
50
- {%- endblock %}
51
-
52
- {%- block subnav %}
53
- {%- endblock %}
54
-
55
- {%- block body %}
56
- {% endblock -%}
70
+ </div>
57
71
  </div>
58
- <div class="column"></div>
59
- </div>
60
- <footer class="footer">
61
- {% block footer %}{% endblock %}
62
- </footer>
72
+
73
+ <footer class="footer">
74
+ {% block footer %}{% endblock %}
75
+ </footer>
63
76
  </body>
64
77
  </html>