meerschaum 2.9.5__py3-none-any.whl → 3.0.0__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.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +33 -4
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +48 -2
- meerschaum/_internal/entry.py +50 -14
- meerschaum/_internal/shell/Shell.py +121 -29
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +359 -0
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +53 -13
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/bootstrap.py +8 -8
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +171 -25
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +143 -6
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +184 -31
- meerschaum/actions/start.py +166 -17
- meerschaum/actions/stop.py +38 -2
- meerschaum/actions/sync.py +7 -2
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +45 -15
- meerschaum/api/_events.py +46 -4
- meerschaum/api/_oauth2.py +162 -9
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/custom.py +4 -3
- meerschaum/api/dash/callbacks/dashboard.py +198 -118
- meerschaum/api/dash/callbacks/jobs.py +14 -7
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +194 -14
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +10 -3
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/tokens.py +389 -0
- meerschaum/api/dash/components.py +36 -15
- meerschaum/api/dash/jobs.py +1 -1
- meerschaum/api/dash/keys.py +35 -93
- meerschaum/api/dash/pages/__init__.py +2 -1
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +16 -5
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/tokens.py +53 -0
- meerschaum/api/dash/pipes.py +382 -95
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +603 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +18 -6
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +91 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +13 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +26 -35
- meerschaum/api/routes/_login.py +120 -15
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +178 -143
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +100 -30
- meerschaum/config/_default.py +132 -64
- meerschaum/config/_edit.py +38 -32
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +10 -8
- meerschaum/config/_paths.py +133 -13
- meerschaum/config/_read_config.py +87 -36
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +37 -15
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +11 -6
- meerschaum/connectors/__init__.py +41 -22
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +12 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +23 -32
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +159 -0
- meerschaum/connectors/instance/_tokens.py +317 -0
- meerschaum/connectors/instance/_users.py +188 -0
- meerschaum/connectors/parse.py +5 -2
- meerschaum/connectors/sql/_SQLConnector.py +22 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +12 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +295 -278
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +46 -21
- meerschaum/connectors/sql/_users.py +36 -2
- meerschaum/connectors/sql/tables/__init__.py +254 -122
- meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
- meerschaum/connectors/valkey/_pipes.py +60 -31
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +115 -85
- meerschaum/core/Pipe/_attributes.py +425 -124
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +96 -68
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +49 -19
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +123 -204
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +35 -10
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +149 -38
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +8 -3
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +301 -88
- meerschaum/plugins/bootstrap.py +510 -4
- meerschaum/utils/_get_pipes.py +97 -30
- meerschaum/utils/daemon/Daemon.py +199 -43
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +47 -6
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/dataframe.py +479 -81
- meerschaum/utils/debug.py +49 -19
- meerschaum/utils/dtypes/__init__.py +476 -34
- meerschaum/utils/dtypes/sql.py +369 -29
- meerschaum/utils/formatting/__init__.py +5 -2
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +52 -50
- meerschaum/utils/formatting/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +44 -18
- meerschaum/utils/misc.py +268 -186
- meerschaum/utils/packages/__init__.py +25 -40
- meerschaum/utils/packages/_packages.py +42 -34
- meerschaum/utils/pipes.py +213 -0
- meerschaum/utils/process.py +2 -2
- meerschaum/utils/prompt.py +175 -144
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +134 -47
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +7 -7
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
- meerschaum-3.0.0.dist-info/RECORD +289 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/_environment.py +0 -145
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,262 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# vim:fenc=utf-8
|
4
|
+
|
5
|
+
"""
|
6
|
+
Patch the runtime configuration from environment variables.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import re
|
11
|
+
import json
|
12
|
+
import contextlib
|
13
|
+
import copy
|
14
|
+
import pathlib
|
15
|
+
|
16
|
+
from meerschaum.utils.typing import List, Union, Dict, Any, Optional
|
17
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
18
|
+
|
19
|
+
|
20
|
+
def apply_environment_patches(env: Optional[Dict[str, Any]] = None) -> None:
|
21
|
+
"""
|
22
|
+
Apply patches defined in `MRSM_CONFIG` and `MRSM_PATCH`.
|
23
|
+
"""
|
24
|
+
config_var = STATIC_CONFIG['environment']['config']
|
25
|
+
patch_var = STATIC_CONFIG['environment']['patch']
|
26
|
+
apply_environment_config(config_var, env=env)
|
27
|
+
apply_environment_config(patch_var, env=env)
|
28
|
+
|
29
|
+
|
30
|
+
def apply_environment_config(env_var: str, env: Optional[Dict[str, Any]] = None) -> None:
|
31
|
+
"""
|
32
|
+
Parse a dictionary (simple or JSON) from an environment variable
|
33
|
+
and apply it to the current configuration.
|
34
|
+
"""
|
35
|
+
from meerschaum.config import get_config, set_config, _config
|
36
|
+
from meerschaum.config._patch import apply_patch_to_config
|
37
|
+
|
38
|
+
env = env if env is not None else os.environ
|
39
|
+
|
40
|
+
if env_var not in env:
|
41
|
+
return
|
42
|
+
|
43
|
+
from meerschaum.utils.misc import string_to_dict
|
44
|
+
try:
|
45
|
+
_patch = string_to_dict(str(os.environ[env_var]).lstrip())
|
46
|
+
except Exception:
|
47
|
+
_patch = None
|
48
|
+
|
49
|
+
error_msg = (
|
50
|
+
f"Environment variable {env_var} is set but cannot be parsed.\n"
|
51
|
+
f"Unset {env_var} or change to JSON or simplified dictionary format "
|
52
|
+
"(see --help, under params for formatting)\n"
|
53
|
+
f"{env_var} is set to:\n{os.environ[env_var]}\n"
|
54
|
+
f"Skipping patching os environment into config..."
|
55
|
+
)
|
56
|
+
|
57
|
+
if not isinstance(_patch, dict):
|
58
|
+
print(error_msg)
|
59
|
+
return
|
60
|
+
|
61
|
+
valids = []
|
62
|
+
|
63
|
+
def load_key(key: str) -> Union[Dict[str, Any], None]:
|
64
|
+
try:
|
65
|
+
c = get_config(key, warn=False)
|
66
|
+
except Exception:
|
67
|
+
c = None
|
68
|
+
return c
|
69
|
+
|
70
|
+
### This was multi-threaded, but I ran into all sorts of locking issues.
|
71
|
+
keys = list(_patch.keys())
|
72
|
+
for key in keys:
|
73
|
+
_ = load_key(key)
|
74
|
+
|
75
|
+
### Load and patch config files.
|
76
|
+
set_config(
|
77
|
+
apply_patch_to_config(
|
78
|
+
_config(),
|
79
|
+
_patch,
|
80
|
+
)
|
81
|
+
)
|
82
|
+
|
83
|
+
|
84
|
+
def apply_environment_uris(env: Optional[Dict[str, Any]] = None) -> None:
|
85
|
+
"""
|
86
|
+
Patch temporary connectors defined in environment variables which start with
|
87
|
+
`MRSM_SQL_` or `MRSM_API_`.
|
88
|
+
"""
|
89
|
+
for env_var in get_connector_env_vars(env=env):
|
90
|
+
apply_connector_uri(env_var, env=env)
|
91
|
+
|
92
|
+
|
93
|
+
def get_connector_env_regex() -> str:
|
94
|
+
"""
|
95
|
+
Return the regex pattern for valid environment variable names for instance connectors.
|
96
|
+
"""
|
97
|
+
return STATIC_CONFIG['environment']['uri_regex']
|
98
|
+
|
99
|
+
|
100
|
+
def get_connector_env_vars(env: Optional[Dict[str, Any]] = None) -> List[str]:
|
101
|
+
"""
|
102
|
+
Get the names of the environment variables which match the Meerschaum connector regex.
|
103
|
+
|
104
|
+
Examples
|
105
|
+
--------
|
106
|
+
>>> get_connector_environment_vars()
|
107
|
+
['MRSM_SQL_FOO']
|
108
|
+
"""
|
109
|
+
uri_regex = get_connector_env_regex()
|
110
|
+
env_vars = []
|
111
|
+
|
112
|
+
env = env if env is not None else os.environ
|
113
|
+
|
114
|
+
for env_var in env:
|
115
|
+
matched = re.match(uri_regex, env_var)
|
116
|
+
if matched is None:
|
117
|
+
continue
|
118
|
+
if env_var in STATIC_CONFIG['environment'].values():
|
119
|
+
continue
|
120
|
+
env_vars.append(env_var)
|
121
|
+
|
122
|
+
return env_vars
|
123
|
+
|
124
|
+
|
125
|
+
def apply_connector_uri(env_var: str, env: Optional[Dict[str, Any]] = None) -> None:
|
126
|
+
"""
|
127
|
+
Parse and validate a URI obtained from an environment variable.
|
128
|
+
"""
|
129
|
+
from meerschaum.config import get_config, set_config, _config
|
130
|
+
from meerschaum.config._patch import apply_patch_to_config
|
131
|
+
from meerschaum.config._read_config import search_and_substitute_config
|
132
|
+
from meerschaum.utils.warnings import warn
|
133
|
+
|
134
|
+
env = env if env is not None else os.environ
|
135
|
+
|
136
|
+
if env_var not in env:
|
137
|
+
return
|
138
|
+
|
139
|
+
uri_regex = get_connector_env_regex()
|
140
|
+
matched = re.match(uri_regex, env_var)
|
141
|
+
groups = matched.groups()
|
142
|
+
typ, label = groups[0].lower(), groups[1].lower()
|
143
|
+
if not typ or not label:
|
144
|
+
return
|
145
|
+
|
146
|
+
uri = env[env_var]
|
147
|
+
|
148
|
+
if uri.lstrip().startswith('{') and uri.rstrip().endswith('}'):
|
149
|
+
try:
|
150
|
+
conn_attrs = json.loads(uri)
|
151
|
+
except Exception:
|
152
|
+
warn(f"Unable to parse JSON for environment connector '{typ}:{label}'.")
|
153
|
+
conn_attrs = {'uri': uri}
|
154
|
+
else:
|
155
|
+
conn_attrs = {'uri': uri}
|
156
|
+
|
157
|
+
set_config(
|
158
|
+
apply_patch_to_config(
|
159
|
+
{'meerschaum': get_config('meerschaum')},
|
160
|
+
{'meerschaum': {'connectors': {typ: {label: conn_attrs}}}},
|
161
|
+
)
|
162
|
+
)
|
163
|
+
|
164
|
+
|
165
|
+
def get_env_vars(env: Optional[Dict[str, Any]] = None) -> List[str]:
|
166
|
+
"""
|
167
|
+
Return all environment variables which begin with `'MRSM_'`.
|
168
|
+
"""
|
169
|
+
prefix = STATIC_CONFIG['environment']['prefix']
|
170
|
+
env = env if env is not None else os.environ
|
171
|
+
return sorted([env_var for env_var in env if env_var.startswith(prefix)])
|
172
|
+
|
173
|
+
|
174
|
+
@contextlib.contextmanager
|
175
|
+
def replace_env(env: Union[Dict[str, Any], None]):
|
176
|
+
"""
|
177
|
+
Temporarily replace environment variables and current configuration.
|
178
|
+
|
179
|
+
Parameters
|
180
|
+
----------
|
181
|
+
env: Dict[str, Any]
|
182
|
+
The new environment dictionary to be patched on `os.environ`.
|
183
|
+
"""
|
184
|
+
if env is None:
|
185
|
+
try:
|
186
|
+
yield
|
187
|
+
finally:
|
188
|
+
return
|
189
|
+
|
190
|
+
from meerschaum.config import _config, set_config
|
191
|
+
from meerschaum.config.paths import (
|
192
|
+
set_root,
|
193
|
+
set_plugins_dir_paths,
|
194
|
+
set_venvs_dir_path,
|
195
|
+
set_config_dir_path,
|
196
|
+
ROOT_DIR_PATH,
|
197
|
+
PLUGINS_DIR_PATHS,
|
198
|
+
VIRTENV_RESOURCES_PATH,
|
199
|
+
CONFIG_DIR_PATH,
|
200
|
+
)
|
201
|
+
|
202
|
+
old_environ = dict(os.environ)
|
203
|
+
old_config = copy.deepcopy(_config())
|
204
|
+
old_root_dir_path = ROOT_DIR_PATH
|
205
|
+
old_plugins_dir_paths = PLUGINS_DIR_PATHS
|
206
|
+
old_venvs_dir_path = VIRTENV_RESOURCES_PATH
|
207
|
+
old_config_dir_path = CONFIG_DIR_PATH
|
208
|
+
|
209
|
+
os.environ.update(env)
|
210
|
+
|
211
|
+
root_dir_env_var = STATIC_CONFIG['environment']['root']
|
212
|
+
plugins_dir_env_var = STATIC_CONFIG['environment']['plugins']
|
213
|
+
config_dir_env_var = STATIC_CONFIG['environment']['config_dir']
|
214
|
+
venvs_dir_env_var = STATIC_CONFIG['environment']['venvs']
|
215
|
+
|
216
|
+
replaced_root = False
|
217
|
+
if root_dir_env_var in env:
|
218
|
+
root_dir_path = pathlib.Path(env[root_dir_env_var])
|
219
|
+
set_root(root_dir_path)
|
220
|
+
replaced_root = True
|
221
|
+
|
222
|
+
replaced_plugins = False
|
223
|
+
if plugins_dir_env_var in env:
|
224
|
+
plugins_dir_paths = env[plugins_dir_env_var]
|
225
|
+
set_plugins_dir_paths(plugins_dir_paths)
|
226
|
+
replaced_plugins = True
|
227
|
+
|
228
|
+
replaced_venvs = False
|
229
|
+
if venvs_dir_env_var in env:
|
230
|
+
venv_dir_path = pathlib.Path(env[venvs_dir_env_var])
|
231
|
+
set_venvs_dir_path(venv_dir_path)
|
232
|
+
replaced_venvs = True
|
233
|
+
|
234
|
+
replaced_config_dir = False
|
235
|
+
if config_dir_env_var in env:
|
236
|
+
config_dir_path = pathlib.Path(env[config_dir_env_var])
|
237
|
+
set_config_dir_path(config_dir_path)
|
238
|
+
replaced_config_dir = True
|
239
|
+
|
240
|
+
apply_environment_patches(env)
|
241
|
+
apply_environment_uris(env)
|
242
|
+
|
243
|
+
try:
|
244
|
+
yield
|
245
|
+
finally:
|
246
|
+
os.environ.clear()
|
247
|
+
os.environ.update(old_environ)
|
248
|
+
|
249
|
+
if replaced_root:
|
250
|
+
set_root(old_root_dir_path)
|
251
|
+
|
252
|
+
if replaced_plugins:
|
253
|
+
set_plugins_dir_paths(old_plugins_dir_paths)
|
254
|
+
|
255
|
+
if replaced_venvs:
|
256
|
+
set_venvs_dir_path(old_venvs_dir_path)
|
257
|
+
|
258
|
+
if replaced_config_dir:
|
259
|
+
set_config_dir_path(old_config_dir_path)
|
260
|
+
|
261
|
+
_config().clear()
|
262
|
+
set_config(old_config)
|
@@ -14,6 +14,8 @@ import json
|
|
14
14
|
from meerschaum.config._paths import (
|
15
15
|
GRAFANA_DATASOURCE_PATH,
|
16
16
|
GRAFANA_DASHBOARD_PATH,
|
17
|
+
DB_INIT_RESOURCES_PATH,
|
18
|
+
DB_CREATE_EXTENSIONS_PATH,
|
17
19
|
ROOT_DIR_PATH,
|
18
20
|
)
|
19
21
|
from meerschaum.config._paths import STACK_COMPOSE_FILENAME, STACK_ENV_FILENAME
|
@@ -39,7 +41,7 @@ valkey_password = 'MRSM{meerschaum:connectors:valkey:main:password}'
|
|
39
41
|
|
40
42
|
env_dict = {
|
41
43
|
'COMPOSE_PROJECT_NAME': 'mrsm',
|
42
|
-
'TIMESCALEDB_VERSION': '
|
44
|
+
'TIMESCALEDB_VERSION': 'pg17',
|
43
45
|
'POSTGRES_USER': db_user,
|
44
46
|
'POSTGRES_PASSWORD': db_pass,
|
45
47
|
'POSTGRES_DB': db_base,
|
@@ -54,19 +56,22 @@ env_dict['MEERSCHAUM_API_CONFIG'] = json.dumps(
|
|
54
56
|
{
|
55
57
|
'meerschaum': 'MRSM{!meerschaum}',
|
56
58
|
'system': 'MRSM{!system}',
|
59
|
+
'api': 'MRSM{!api}',
|
57
60
|
},
|
58
61
|
indent=4,
|
59
62
|
).replace(
|
60
63
|
'"MRSM{!system}"', 'MRSM{!system}'
|
61
64
|
).replace(
|
62
|
-
'"MRSM{!meerschaum}"', 'MRSM{!meerschaum}'
|
65
|
+
'"MRSM{!meerschaum}"', 'MRSM{!meerschaum}'
|
66
|
+
).replace(
|
67
|
+
'"MRSM{!api}"', 'MRSM{!api}'
|
63
68
|
)
|
64
69
|
|
65
70
|
volumes = {
|
66
71
|
'api_root': '/meerschaum',
|
67
|
-
'meerschaum_db_data': '/
|
72
|
+
'meerschaum_db_data': '/home/postgres/pgdata',
|
68
73
|
'grafana_storage': '/var/lib/grafana',
|
69
|
-
'valkey_data': '/
|
74
|
+
'valkey_data': '/valkey/data',
|
70
75
|
}
|
71
76
|
networks = {
|
72
77
|
'frontend': None,
|
@@ -121,7 +126,7 @@ default_docker_compose_config = {
|
|
121
126
|
'POSTGRES_PASSWORD': '<DOLLAR>POSTGRES_PASSWORD',
|
122
127
|
'ALLOW_IP_RANGE': env_dict['ALLOW_IP_RANGE'],
|
123
128
|
},
|
124
|
-
'command': 'postgres -c max_connections=1000 -c shared_buffers=1024MB',
|
129
|
+
'command': 'postgres -c max_connections=1000 -c shared_buffers=1024MB -c max_prepared_transactions=100',
|
125
130
|
'healthcheck': {
|
126
131
|
'test': [
|
127
132
|
'CMD-SHELL', 'pg_isready -d <DOLLAR>POSTGRES_DB -U <DOLLAR>POSTGRES_USER',
|
@@ -131,13 +136,14 @@ default_docker_compose_config = {
|
|
131
136
|
'retries': 5
|
132
137
|
},
|
133
138
|
'restart': 'always',
|
134
|
-
'image': 'timescale/timescaledb:' + env_dict['TIMESCALEDB_VERSION'],
|
139
|
+
'image': 'timescale/timescaledb-ha:' + env_dict['TIMESCALEDB_VERSION'],
|
135
140
|
'ports': [
|
136
141
|
f'{db_port}:5432',
|
137
142
|
],
|
138
143
|
'hostname': db_hostname,
|
139
144
|
'volumes': [
|
140
145
|
'meerschaum_db_data:' + volumes['meerschaum_db_data'],
|
146
|
+
f'{DB_INIT_RESOURCES_PATH.as_posix()}:/docker-entrypoint-initdb.d:z,ro',
|
141
147
|
],
|
142
148
|
'shm_size': '1024m',
|
143
149
|
'networks': [
|
@@ -180,7 +186,7 @@ default_docker_compose_config = {
|
|
180
186
|
],
|
181
187
|
},
|
182
188
|
'valkey': {
|
183
|
-
'image': '
|
189
|
+
'image': 'valkey/valkey:latest',
|
184
190
|
'restart': 'always',
|
185
191
|
'environment': {
|
186
192
|
'VALKEY_PASSWORD': '<DOLLAR>VALKEY_PASSWORD',
|
@@ -224,8 +230,8 @@ default_docker_compose_config = {
|
|
224
230
|
'volumes': [
|
225
231
|
'grafana_storage' + ':' + volumes['grafana_storage'],
|
226
232
|
### NOTE: Mount with the 'z' option for SELinux.
|
227
|
-
f'{GRAFANA_DATASOURCE_PATH.parent}:/etc/grafana/provisioning/datasources:z,ro',
|
228
|
-
f'{GRAFANA_DASHBOARD_PATH.parent}:/etc/grafana/provisioning/dashboards:z,ro',
|
233
|
+
f'{GRAFANA_DATASOURCE_PATH.parent.as_posix()}:/etc/grafana/provisioning/datasources:z,ro',
|
234
|
+
f'{GRAFANA_DASHBOARD_PATH.parent.as_posix()}:/etc/grafana/provisioning/dashboards:z,ro',
|
229
235
|
],
|
230
236
|
'environment': {
|
231
237
|
'GF_SECURITY_ALLOW_EMBEDDING': 'true',
|
@@ -273,6 +279,21 @@ def _sync_stack_files():
|
|
273
279
|
substitute = True,
|
274
280
|
)
|
275
281
|
|
282
|
+
_write_initdb()
|
283
|
+
|
284
|
+
def _write_initdb():
|
285
|
+
create_postgis_text = (
|
286
|
+
"CREATE EXTENSION IF NOT EXISTS timescaledb;\n"
|
287
|
+
"CREATE EXTENSION IF NOT EXISTS postgis;\n"
|
288
|
+
"CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit;\n"
|
289
|
+
"CREATE EXTENSION IF NOT EXISTS pg_stat_statements;\n"
|
290
|
+
)
|
291
|
+
if DB_CREATE_EXTENSIONS_PATH.exists():
|
292
|
+
return
|
293
|
+
|
294
|
+
with open(DB_CREATE_EXTENSIONS_PATH, 'w+', encoding='utf-8') as f:
|
295
|
+
f.write(create_postgis_text)
|
296
|
+
|
276
297
|
NECESSARY_FILES = [STACK_COMPOSE_PATH, GRAFANA_DATASOURCE_PATH, GRAFANA_DASHBOARD_PATH]
|
277
298
|
def get_necessary_files():
|
278
299
|
from meerschaum.config import get_config
|
@@ -286,19 +307,20 @@ def get_necessary_files():
|
|
286
307
|
|
287
308
|
|
288
309
|
def write_stack(
|
289
|
-
|
290
|
-
|
310
|
+
debug: bool = False
|
311
|
+
):
|
291
312
|
"""Write Docker Compose configuration files."""
|
292
313
|
from meerschaum.config._edit import general_write_yaml_config
|
293
314
|
from meerschaum.config._sync import sync_files
|
294
315
|
general_write_yaml_config(get_necessary_files(), debug=debug)
|
295
316
|
return sync_files(['stack'])
|
296
317
|
|
318
|
+
|
297
319
|
def edit_stack(
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
320
|
+
action: Optional[List[str]] = None,
|
321
|
+
debug: bool = False,
|
322
|
+
**kw
|
323
|
+
):
|
302
324
|
"""Open docker-compose.yaml or .env for editing."""
|
303
325
|
from meerschaum.config._edit import general_edit_config
|
304
326
|
if action is None:
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#! /usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# vim:fenc=utf-8
|
4
|
+
|
5
|
+
"""
|
6
|
+
Alias import for the internal static configuration dictionary.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from meerschaum._internal.static import SERVER_ID, STATIC_CONFIG
|
10
|
+
|
11
|
+
__all__ = ('STATIC_CONFIG',)
|
12
|
+
|
13
|
+
|
14
|
+
def _static_config():
|
15
|
+
"""
|
16
|
+
Alias function for the global `STATIC_CONFIG` dictionary.
|
17
|
+
"""
|
18
|
+
return STATIC_CONFIG
|
@@ -7,10 +7,12 @@ Define the parent `Connector` class.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
10
11
|
import abc
|
11
12
|
import copy
|
12
13
|
from meerschaum.utils.typing import Iterable, Optional, Any, Union, List, Dict
|
13
14
|
|
15
|
+
|
14
16
|
class InvalidAttributesError(Exception):
|
15
17
|
"""
|
16
18
|
Raised when the incorrect attributes are set in the Connector.
|
@@ -20,6 +22,9 @@ class Connector(metaclass=abc.ABCMeta):
|
|
20
22
|
"""
|
21
23
|
The base connector class to hold connection attributes.
|
22
24
|
"""
|
25
|
+
|
26
|
+
IS_INSTANCE: bool = False
|
27
|
+
|
23
28
|
def __init__(
|
24
29
|
self,
|
25
30
|
type: Optional[str] = None,
|
@@ -70,7 +75,7 @@ class Connector(metaclass=abc.ABCMeta):
|
|
70
75
|
inherit_default: bool = True,
|
71
76
|
**kw: Any
|
72
77
|
):
|
73
|
-
from meerschaum.
|
78
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
74
79
|
from meerschaum.utils.warnings import error
|
75
80
|
|
76
81
|
self._attributes = {}
|
@@ -147,11 +152,10 @@ class Connector(metaclass=abc.ABCMeta):
|
|
147
152
|
------
|
148
153
|
An error if any of the required attributes are missing.
|
149
154
|
"""
|
150
|
-
from meerschaum.utils.warnings import error
|
151
|
-
from meerschaum.utils.debug import dprint
|
155
|
+
from meerschaum.utils.warnings import error
|
152
156
|
from meerschaum.utils.misc import items_str
|
153
157
|
if required_attributes is None:
|
154
|
-
required_attributes = ['label']
|
158
|
+
required_attributes = ['type', 'label']
|
155
159
|
|
156
160
|
missing_attributes = set()
|
157
161
|
for a in required_attributes:
|
@@ -213,6 +217,8 @@ class Connector(metaclass=abc.ABCMeta):
|
|
213
217
|
else r'executor$'
|
214
218
|
)
|
215
219
|
_type = re.sub(suffix_regex, '', self.__class__.__name__.lower())
|
220
|
+
if not _type or _type.lower() == 'instance':
|
221
|
+
raise ValueError("No type could be determined for this connector.")
|
216
222
|
self.__dict__['type'] = _type
|
217
223
|
return _type
|
218
224
|
|
@@ -224,8 +230,7 @@ class Connector(metaclass=abc.ABCMeta):
|
|
224
230
|
"""
|
225
231
|
_label = self.__dict__.get('label', None)
|
226
232
|
if _label is None:
|
227
|
-
from meerschaum.
|
233
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
228
234
|
_label = STATIC_CONFIG['connectors']['default_label']
|
229
235
|
self.__dict__['label'] = _label
|
230
236
|
return _label
|
231
|
-
|
@@ -14,18 +14,19 @@ For ease of use, you can also import from the root `meerschaum` module:
|
|
14
14
|
from __future__ import annotations
|
15
15
|
|
16
16
|
import meerschaum as mrsm
|
17
|
-
from meerschaum.utils.typing import Any, Union, List, Dict
|
17
|
+
from meerschaum.utils.typing import Any, Union, List, Dict, Optional
|
18
18
|
from meerschaum.utils.threading import RLock
|
19
19
|
from meerschaum.utils.warnings import warn
|
20
20
|
|
21
21
|
from meerschaum.connectors._Connector import Connector, InvalidAttributesError
|
22
|
+
from meerschaum.connectors.instance._InstanceConnector import InstanceConnector
|
22
23
|
from meerschaum.connectors.sql._SQLConnector import SQLConnector
|
23
24
|
from meerschaum.connectors.api._APIConnector import APIConnector
|
24
|
-
from meerschaum.connectors.sql._create_engine import flavor_configs as sql_flavor_configs
|
25
25
|
|
26
26
|
__all__ = (
|
27
27
|
"make_connector",
|
28
28
|
"Connector",
|
29
|
+
"InstanceConnector",
|
29
30
|
"SQLConnector",
|
30
31
|
"APIConnector",
|
31
32
|
"get_connector",
|
@@ -50,30 +51,16 @@ _locks: Dict[str, RLock] = {
|
|
50
51
|
'connectors' : RLock(),
|
51
52
|
'types' : RLock(),
|
52
53
|
'custom_types' : RLock(),
|
54
|
+
'plugins_types' : RLock(),
|
53
55
|
'_loaded_plugin_connectors': RLock(),
|
54
56
|
'instance_types' : RLock(),
|
55
57
|
}
|
56
|
-
|
57
|
-
'api': {
|
58
|
-
'required': [
|
59
|
-
'host',
|
60
|
-
'username',
|
61
|
-
'password',
|
62
|
-
],
|
63
|
-
'optional': [
|
64
|
-
'port',
|
65
|
-
],
|
66
|
-
'default': {
|
67
|
-
'protocol': 'http',
|
68
|
-
},
|
69
|
-
},
|
70
|
-
'sql': {
|
71
|
-
'flavors': sql_flavor_configs,
|
72
|
-
},
|
73
|
-
}
|
58
|
+
|
74
59
|
### Fill this with objects only when connectors are first referenced.
|
75
60
|
types: Dict[str, Any] = {}
|
76
61
|
custom_types: set = set()
|
62
|
+
plugins_types: Dict[str, List[str]] = {}
|
63
|
+
_known_custom_types: set = set()
|
77
64
|
_loaded_plugin_connectors: bool = False
|
78
65
|
|
79
66
|
|
@@ -88,7 +75,6 @@ def get_connector(
|
|
88
75
|
Return existing connector or create new connection and store for reuse.
|
89
76
|
|
90
77
|
You can create new connectors if enough parameters are provided for the given type and flavor.
|
91
|
-
|
92
78
|
|
93
79
|
Parameters
|
94
80
|
----------
|
@@ -130,7 +116,7 @@ def get_connector(
|
|
130
116
|
"""
|
131
117
|
from meerschaum.connectors.parse import parse_instance_keys
|
132
118
|
from meerschaum.config import get_config
|
133
|
-
from meerschaum.
|
119
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
134
120
|
from meerschaum.utils.warnings import warn
|
135
121
|
global _loaded_plugin_connectors
|
136
122
|
if isinstance(type, str) and not label and ':' in type:
|
@@ -313,16 +299,23 @@ def make_connector(cls, _is_executor: bool = False):
|
|
313
299
|
>>>
|
314
300
|
"""
|
315
301
|
import re
|
302
|
+
from meerschaum.plugins import _get_parent_plugin
|
316
303
|
suffix_regex = (
|
317
304
|
r'connector$'
|
318
305
|
if not _is_executor
|
319
306
|
else r'executor$'
|
320
307
|
)
|
308
|
+
plugin_name = _get_parent_plugin(2)
|
321
309
|
typ = re.sub(suffix_regex, '', cls.__name__.lower())
|
322
310
|
with _locks['types']:
|
323
311
|
types[typ] = cls
|
324
312
|
with _locks['custom_types']:
|
325
313
|
custom_types.add(typ)
|
314
|
+
if plugin_name:
|
315
|
+
with _locks['plugins_types']:
|
316
|
+
if plugin_name not in plugins_types:
|
317
|
+
plugins_types[plugin_name] = []
|
318
|
+
plugins_types[plugin_name].append(typ)
|
326
319
|
with _locks['connectors']:
|
327
320
|
if typ not in connectors:
|
328
321
|
connectors[typ] = {}
|
@@ -353,6 +346,30 @@ def load_plugin_connectors():
|
|
353
346
|
import_plugins(*to_import)
|
354
347
|
|
355
348
|
|
349
|
+
def unload_plugin_connectors(
|
350
|
+
plugin_names: Optional[List[str]] = None,
|
351
|
+
debug: bool = False,
|
352
|
+
) -> None:
|
353
|
+
"""
|
354
|
+
Unload custom connectors added by plugins.
|
355
|
+
"""
|
356
|
+
from meerschaum.plugins import get_plugins_names
|
357
|
+
global custom_types, _known_custom_types, types, plugins_types, connectors
|
358
|
+
|
359
|
+
plugin_names = plugin_names or get_plugins_names()
|
360
|
+
|
361
|
+
for plugin_name in plugin_names:
|
362
|
+
plugin_types = plugins_types.get(plugin_name, [])
|
363
|
+
for typ in plugin_types:
|
364
|
+
_ = types.pop(typ, None)
|
365
|
+
_ = connectors.pop(typ, None)
|
366
|
+
if typ in instance_types:
|
367
|
+
instance_types.remove(typ)
|
368
|
+
|
369
|
+
custom_types.clear()
|
370
|
+
custom_types.update(_known_custom_types)
|
371
|
+
|
372
|
+
|
356
373
|
def get_connector_plugin(
|
357
374
|
connector: Connector,
|
358
375
|
) -> Union[str, None, mrsm.Plugin]:
|
@@ -389,3 +406,5 @@ def _load_builtin_custom_connectors():
|
|
389
406
|
"""
|
390
407
|
import meerschaum.jobs.systemd
|
391
408
|
import meerschaum.connectors.valkey
|
409
|
+
_known_custom_types.add('valkey')
|
410
|
+
_known_custom_types.add('systemd')
|
@@ -10,7 +10,7 @@ from __future__ import annotations
|
|
10
10
|
|
11
11
|
from datetime import datetime, timedelta, timezone
|
12
12
|
from meerschaum.utils.typing import Optional, List, Union
|
13
|
-
from meerschaum.connectors import
|
13
|
+
from meerschaum.connectors import InstanceConnector
|
14
14
|
from meerschaum.utils.warnings import warn, error
|
15
15
|
from meerschaum.utils.packages import attempt_import
|
16
16
|
|
@@ -18,15 +18,13 @@ required_attributes = {
|
|
18
18
|
'host',
|
19
19
|
}
|
20
20
|
|
21
|
-
class APIConnector(
|
21
|
+
class APIConnector(InstanceConnector):
|
22
22
|
"""
|
23
23
|
Connect to a Meerschaum API instance.
|
24
24
|
"""
|
25
25
|
|
26
|
-
IS_INSTANCE: bool = True
|
27
26
|
IS_THREAD_SAFE: bool = False
|
28
|
-
|
29
|
-
OPTIONAL_ATTRIBUTES: List[str] = ['port']
|
27
|
+
OPTIONAL_ATTRIBUTES: List[str] = ['port', 'client_secret', 'client_id', 'api_key']
|
30
28
|
|
31
29
|
from ._request import (
|
32
30
|
make_request,
|
@@ -82,6 +80,16 @@ class APIConnector(Connector):
|
|
82
80
|
get_user_type,
|
83
81
|
get_user_attributes,
|
84
82
|
)
|
83
|
+
from ._tokens import (
|
84
|
+
register_token,
|
85
|
+
get_token_model,
|
86
|
+
get_tokens,
|
87
|
+
edit_token,
|
88
|
+
invalidate_token,
|
89
|
+
get_token_scopes,
|
90
|
+
token_exists,
|
91
|
+
delete_token,
|
92
|
+
)
|
85
93
|
from ._uri import from_uri
|
86
94
|
from ._jobs import (
|
87
95
|
get_jobs,
|
@@ -154,9 +162,15 @@ class APIConnector(Connector):
|
|
154
162
|
"""
|
155
163
|
Return the fully qualified URI.
|
156
164
|
"""
|
165
|
+
import urllib.parse
|
157
166
|
username = self.__dict__.get('username', None)
|
158
167
|
password = self.__dict__.get('password', None)
|
168
|
+
client_id = self.__dict__.get('client_id', None)
|
169
|
+
client_secret = self.__dict__.get('client_secret', None)
|
170
|
+
api_key = self.__dict__.get('api_key', None)
|
159
171
|
creds = (username + ':' + password + '@') if username and password else ''
|
172
|
+
params = {}
|
173
|
+
params_str = ('?' + urllib.parse.urlencode(params)) if params else ''
|
160
174
|
return (
|
161
175
|
self.protocol
|
162
176
|
+ '://'
|
@@ -167,9 +181,9 @@ class APIConnector(Connector):
|
|
167
181
|
if self.__dict__.get('port', None)
|
168
182
|
else ''
|
169
183
|
)
|
184
|
+
+ params_str
|
170
185
|
)
|
171
186
|
|
172
|
-
|
173
187
|
@property
|
174
188
|
def session(self):
|
175
189
|
if self._session is None:
|
@@ -206,3 +220,17 @@ class APIConnector(Connector):
|
|
206
220
|
Return the instance keys to be sent alongside pipe requests.
|
207
221
|
"""
|
208
222
|
return self._instance_keys
|
223
|
+
|
224
|
+
@property
|
225
|
+
def login_scheme(self) -> str:
|
226
|
+
"""
|
227
|
+
Return the login scheme to use based on the configured credentials.
|
228
|
+
"""
|
229
|
+
if 'username' in self.__dict__:
|
230
|
+
return 'password'
|
231
|
+
if 'client_id' in self.__dict__:
|
232
|
+
return 'client_credentials'
|
233
|
+
elif 'api_key' in self.__dict__:
|
234
|
+
return 'api_key'
|
235
|
+
|
236
|
+
return 'password'
|