meerschaum 2.2.0rc1__py3-none-any.whl → 2.2.0rc2__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/__main__.py +1 -1
- meerschaum/actions/show.py +68 -43
- meerschaum/api/dash/callbacks/dashboard.py +2 -7
- meerschaum/api/dash/pipes.py +33 -9
- meerschaum/api/dash/plugins.py +25 -9
- meerschaum/api/resources/templates/termpage.html +3 -0
- meerschaum/api/routes/_login.py +5 -4
- meerschaum/api/routes/_plugins.py +6 -3
- meerschaum/config/_dash.py +11 -0
- meerschaum/config/_default.py +3 -1
- meerschaum/config/_jobs.py +10 -4
- meerschaum/config/_paths.py +1 -0
- meerschaum/config/_sync.py +2 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +6 -6
- meerschaum/config/stack/grafana/__init__.py +1 -1
- meerschaum/config/static/__init__.py +3 -1
- meerschaum/connectors/sql/_plugins.py +0 -2
- meerschaum/core/User/_User.py +156 -16
- meerschaum/core/User/__init__.py +1 -1
- meerschaum/plugins/_Plugin.py +1 -1
- meerschaum/utils/daemon/Daemon.py +23 -12
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +46 -4
- meerschaum/utils/daemon/RotatingFile.py +76 -57
- meerschaum/utils/daemon/__init__.py +1 -0
- meerschaum/utils/threading.py +1 -0
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/METADATA +1 -1
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/RECORD +34 -33
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/WHEEL +0 -0
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.0rc1.dist-info → meerschaum-2.2.0rc2.dist-info}/zip-safe +0 -0
meerschaum/__main__.py
CHANGED
meerschaum/actions/show.py
CHANGED
@@ -578,7 +578,7 @@ def _show_logs(
|
|
578
578
|
`show logs myjob myotherjob`
|
579
579
|
"""
|
580
580
|
import os, pathlib, random, asyncio
|
581
|
-
from datetime import datetime
|
581
|
+
from datetime import datetime, timezone
|
582
582
|
from meerschaum.utils.packages import attempt_import, import_rich
|
583
583
|
from meerschaum.utils.daemon import get_filtered_daemons, Daemon
|
584
584
|
from meerschaum.utils.warnings import warn, info
|
@@ -589,81 +589,106 @@ def _show_logs(
|
|
589
589
|
if not ANSI:
|
590
590
|
info = print
|
591
591
|
colors = get_config('jobs', 'logs', 'colors')
|
592
|
+
timestamp_format = get_config('jobs', 'logs', 'timestamp_format')
|
593
|
+
follow_timestamp_format = get_config('jobs', 'logs', 'follow_timestamp_format')
|
592
594
|
daemons = get_filtered_daemons(action)
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
595
|
+
now = datetime.now(timezone.utc)
|
596
|
+
now_str = now.strftime(timestamp_format)
|
597
|
+
now_follow_str = now.strftime(follow_timestamp_format)
|
598
|
+
|
599
|
+
def build_buffer_spaces(daemons) -> Dict[str, str]:
|
600
|
+
max_len_id = max(len(d.daemon_id) for d in daemons) if daemons else 0
|
601
|
+
buffer_len = max(
|
602
|
+
get_config('jobs', 'logs', 'min_buffer_len'),
|
603
|
+
max_len_id
|
604
|
+
)
|
597
605
|
return {
|
598
|
-
d.daemon_id: ''.join([' ' for i in range(
|
606
|
+
d.daemon_id: ''.join([' ' for i in range(buffer_len - len(d.daemon_id))])
|
599
607
|
for d in daemons
|
600
608
|
}
|
601
609
|
|
602
|
-
def
|
610
|
+
def build_job_colors(daemons, _old_job_colors = None) -> Dict[str, str]:
|
603
611
|
return {d.daemon_id: colors[i % len(colors)] for i, d in enumerate(daemons)}
|
604
612
|
|
605
|
-
|
606
|
-
|
613
|
+
buffer_spaces = build_buffer_spaces(daemons)
|
614
|
+
job_colors = build_job_colors(daemons)
|
607
615
|
|
608
|
-
def
|
609
|
-
nonlocal
|
610
|
-
if daemon_id not in
|
616
|
+
def get_buffer_spaces(daemon_id):
|
617
|
+
nonlocal buffer_spaces, daemons
|
618
|
+
if daemon_id not in buffer_spaces:
|
611
619
|
d = Daemon(daemon_id=daemon_id)
|
612
620
|
if d not in daemons:
|
613
621
|
daemons = get_filtered_daemons(action)
|
614
|
-
|
615
|
-
return
|
622
|
+
buffer_spaces = build_buffer_spaces(daemons)
|
623
|
+
return buffer_spaces[daemon_id] or ' '
|
616
624
|
|
617
|
-
def
|
618
|
-
nonlocal
|
619
|
-
if daemon_id not in
|
625
|
+
def get_job_colors(daemon_id):
|
626
|
+
nonlocal job_colors, daemons
|
627
|
+
if daemon_id not in job_colors:
|
620
628
|
d = Daemon(daemon_id=daemon_id)
|
621
629
|
if d not in daemons:
|
622
630
|
daemons = get_filtered_daemons(action)
|
623
|
-
|
624
|
-
return
|
631
|
+
job_colors = build_job_colors(daemons)
|
632
|
+
return job_colors[daemon_id]
|
625
633
|
|
626
|
-
def
|
634
|
+
def follow_pretty_print():
|
627
635
|
watchfiles = attempt_import('watchfiles')
|
628
636
|
rich = import_rich()
|
629
637
|
rich_text = attempt_import('rich.text')
|
630
|
-
|
638
|
+
watch_daemon_ids = {d.daemon_id: d for d in daemons}
|
631
639
|
info("Watching log files...")
|
632
640
|
|
633
|
-
|
634
|
-
|
641
|
+
previous_line_timestamp = None
|
642
|
+
def print_job_line(daemon, line):
|
643
|
+
nonlocal previous_line_timestamp
|
644
|
+
date_prefix_str = line[:len(now_str)]
|
635
645
|
try:
|
636
|
-
line_timestamp = datetime.
|
646
|
+
line_timestamp = datetime.strptime(date_prefix_str, timestamp_format)
|
647
|
+
previous_line_timestamp = line_timestamp
|
637
648
|
except Exception as e:
|
638
649
|
line_timestamp = None
|
639
650
|
if line_timestamp:
|
640
|
-
line = line[len(
|
641
|
-
|
651
|
+
line = line[(len(now_str) + 3):]
|
652
|
+
else:
|
653
|
+
line_timestamp = previous_line_timestamp
|
654
|
+
|
655
|
+
if len(line) == 0 or line == '\n':
|
642
656
|
return
|
657
|
+
|
643
658
|
text = rich_text.Text(daemon.daemon_id)
|
644
|
-
|
645
|
-
|
646
|
-
+ (
|
659
|
+
line_prefix = (
|
660
|
+
get_buffer_spaces(daemon.daemon_id)
|
661
|
+
+ (line_timestamp.strftime(follow_timestamp_format) if line_timestamp else '')
|
662
|
+
+ ' | '
|
647
663
|
)
|
664
|
+
text.append(line_prefix + (line[:-1] if line[-1] == '\n' else line))
|
648
665
|
if ANSI:
|
649
666
|
text.stylize(
|
650
|
-
|
667
|
+
get_job_colors(daemon.daemon_id),
|
651
668
|
0,
|
652
|
-
len(daemon.daemon_id) + len(
|
669
|
+
len(daemon.daemon_id) + len(line_prefix)
|
653
670
|
)
|
654
671
|
get_console().print(text)
|
655
672
|
|
656
673
|
|
657
|
-
def
|
674
|
+
def print_log_lines(daemon):
|
658
675
|
for line in daemon.readlines():
|
659
|
-
|
676
|
+
print_job_line(daemon, line)
|
660
677
|
|
661
|
-
def
|
678
|
+
def seek_back_offset(d) -> bool:
|
662
679
|
if d.log_offset_path.exists():
|
663
680
|
d.log_offset_path.unlink()
|
664
681
|
|
665
682
|
latest_subfile_path = d.rotating_log.get_latest_subfile_path()
|
666
683
|
latest_subfile_index = d.rotating_log.get_latest_subfile_index()
|
684
|
+
|
685
|
+
### Sometimes the latest file is empty.
|
686
|
+
if os.stat(latest_subfile_path).st_size == 0 and latest_subfile_index > 0:
|
687
|
+
latest_subfile_index -= 1
|
688
|
+
latest_subfile_path = d.rotating_log.get_subfile_path_from_index(
|
689
|
+
latest_subfile_index
|
690
|
+
)
|
691
|
+
|
667
692
|
with open(latest_subfile_path, 'r', encoding='utf-8') as f:
|
668
693
|
latest_lines = f.readlines()
|
669
694
|
|
@@ -683,11 +708,11 @@ def _show_logs(
|
|
683
708
|
for d in daemons
|
684
709
|
}
|
685
710
|
for d in daemons:
|
686
|
-
|
687
|
-
|
711
|
+
seek_back_offset(d)
|
712
|
+
print_log_lines(d)
|
688
713
|
|
689
714
|
_quit = False
|
690
|
-
def
|
715
|
+
def watch_logs():
|
691
716
|
for changes in watchfiles.watch(LOGS_RESOURCES_PATH):
|
692
717
|
if _quit:
|
693
718
|
return
|
@@ -699,7 +724,7 @@ def _show_logs(
|
|
699
724
|
if not file_path.exists():
|
700
725
|
continue
|
701
726
|
daemon_id = file_path.name.split('.log')[0]
|
702
|
-
if daemon_id not in
|
727
|
+
if daemon_id not in watch_daemon_ids and action:
|
703
728
|
continue
|
704
729
|
try:
|
705
730
|
daemon = Daemon(daemon_id=daemon_id)
|
@@ -708,21 +733,21 @@ def _show_logs(
|
|
708
733
|
warn(f"Seeing new logs for non-existent job '{daemon_id}'.", stack=False)
|
709
734
|
|
710
735
|
if daemon is not None:
|
711
|
-
|
736
|
+
print_log_lines(daemon)
|
712
737
|
|
713
738
|
try:
|
714
|
-
|
739
|
+
watch_logs()
|
715
740
|
except KeyboardInterrupt as ki:
|
716
741
|
_quit = True
|
717
742
|
|
718
|
-
def
|
743
|
+
def print_nopretty_log_text():
|
719
744
|
for d in daemons:
|
720
745
|
log_text = d.log_text
|
721
746
|
print(d.daemon_id)
|
722
747
|
print(log_text)
|
723
748
|
|
724
|
-
|
725
|
-
|
749
|
+
print_log_text = follow_pretty_print if not nopretty else print_nopretty_log_text
|
750
|
+
print_log_text()
|
726
751
|
|
727
752
|
return True, "Success"
|
728
753
|
|
@@ -440,18 +440,13 @@ def update_flags(input_flags_dropdown_values, n_clicks, input_flags_texts):
|
|
440
440
|
className = 'input-text',
|
441
441
|
)
|
442
442
|
|
443
|
+
remove_index = trigger_dict['index'] if trigger_type == 'input-flags-remove-button' else None
|
443
444
|
rows = [
|
444
445
|
build_row(i, val, val_text)
|
445
446
|
for i, (val, val_text) in enumerate(zip(input_flags_dropdown_values, input_flags_texts))
|
447
|
+
if i != remove_index
|
446
448
|
]
|
447
449
|
|
448
|
-
if trigger_type == 'input-flags-remove-button':
|
449
|
-
remove_index = trigger_dict['index']
|
450
|
-
try:
|
451
|
-
del rows[remove_index]
|
452
|
-
except IndexError:
|
453
|
-
pass
|
454
|
-
|
455
450
|
if not rows or input_flags_dropdown_values[-1]:
|
456
451
|
rows.append(build_row(len(rows), None, None))
|
457
452
|
|
meerschaum/api/dash/pipes.py
CHANGED
@@ -24,6 +24,7 @@ from meerschaum.api.dash import (
|
|
24
24
|
from meerschaum.api.dash.connectors import get_web_connector
|
25
25
|
from meerschaum.api.dash.components import alert_from_success_tuple
|
26
26
|
from meerschaum.api.dash.users import is_session_authenticated
|
27
|
+
from meerschaum.config import get_config
|
27
28
|
import meerschaum as mrsm
|
28
29
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
29
30
|
dash_ace = attempt_import('dash_ace', lazy=False, check_update=CHECK_UPDATE)
|
@@ -110,12 +111,16 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
110
111
|
session_id = (session_data or {}).get('session-id', None)
|
111
112
|
authenticated = is_session_authenticated(str(session_id))
|
112
113
|
|
113
|
-
|
114
|
-
alerts = [alert_from_success_tuple(
|
115
|
-
if not isinstance(
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
pipes = pipes_from_state(*keys, as_list=True)
|
115
|
+
alerts = [alert_from_success_tuple(pipes)]
|
116
|
+
if not isinstance(pipes, list):
|
117
|
+
pipes = []
|
118
|
+
|
119
|
+
max_num_pipes_cards = get_config('dash', 'max_num_pipes_cards')
|
120
|
+
overflow_pipes = pipes[max_num_pipes_cards:]
|
121
|
+
|
122
|
+
for pipe in pipes[:max_num_pipes_cards]:
|
123
|
+
meta_str = json.dumps(pipe.meta)
|
119
124
|
footer_children = dbc.Row(
|
120
125
|
[
|
121
126
|
dbc.Col(
|
@@ -188,13 +193,13 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
188
193
|
)
|
189
194
|
card_body_children = [
|
190
195
|
html.H5(
|
191
|
-
html.B(str(
|
196
|
+
html.B(str(pipe)),
|
192
197
|
className = 'card-title',
|
193
198
|
style = {'font-family': ['monospace']}
|
194
199
|
),
|
195
200
|
html.Div(
|
196
201
|
dbc.Accordion(
|
197
|
-
accordion_items_from_pipe(
|
202
|
+
accordion_items_from_pipe(pipe, authenticated=authenticated),
|
198
203
|
flush = True,
|
199
204
|
start_collapsed = True,
|
200
205
|
id = {'type': 'pipe-accordion', 'index': meta_str},
|
@@ -203,11 +208,30 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
203
208
|
|
204
209
|
]
|
205
210
|
cards.append(
|
206
|
-
dbc.Card(
|
211
|
+
dbc.Card([
|
207
212
|
dbc.CardBody(children=card_body_children),
|
208
213
|
dbc.CardFooter(children=footer_children),
|
209
214
|
])
|
210
215
|
)
|
216
|
+
|
217
|
+
if overflow_pipes:
|
218
|
+
cards.append(
|
219
|
+
dbc.Card([
|
220
|
+
dbc.CardBody(
|
221
|
+
html.Ul(
|
222
|
+
[
|
223
|
+
html.Li(html.H5(
|
224
|
+
html.B(str(pipe)),
|
225
|
+
className = 'card-title',
|
226
|
+
style = {'font-family': ['monospace']}
|
227
|
+
))
|
228
|
+
for pipe in overflow_pipes
|
229
|
+
]
|
230
|
+
)
|
231
|
+
)
|
232
|
+
])
|
233
|
+
)
|
234
|
+
|
211
235
|
return cards, alerts
|
212
236
|
|
213
237
|
|
meerschaum/api/dash/plugins.py
CHANGED
@@ -32,17 +32,22 @@ def get_plugins_cards(
|
|
32
32
|
desc = get_api_connector().get_plugin_attributes(plugin).get(
|
33
33
|
'description', 'No description provided.'
|
34
34
|
)
|
35
|
-
desc_textarea_kw =
|
36
|
-
value
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
desc_textarea_kw = {
|
36
|
+
'value': desc,
|
37
|
+
'readOnly': True,
|
38
|
+
'debounce': False,
|
39
|
+
'className': 'plugin-description',
|
40
|
+
'draggable': False,
|
41
|
+
'wrap': 'overflow',
|
42
|
+
'placeholder': "Edit the plugin's description",
|
43
|
+
'id': {'type': 'description-textarea', 'index': plugin_name},
|
44
|
+
}
|
40
45
|
|
41
46
|
card_body_children = [html.H4(plugin_name)]
|
42
47
|
|
43
48
|
if is_plugin_owner(plugin_name, session_data):
|
44
49
|
desc_textarea_kw['readOnly'] = False
|
45
|
-
card_body_children
|
50
|
+
card_body_children.append(dbc.Textarea(**desc_textarea_kw))
|
46
51
|
if not desc_textarea_kw['readOnly']:
|
47
52
|
card_body_children += [
|
48
53
|
dbc.Button(
|
@@ -53,12 +58,23 @@ def get_plugins_cards(
|
|
53
58
|
),
|
54
59
|
html.Div(id={'type': 'edit-alert-div', 'index': plugin_name}),
|
55
60
|
]
|
56
|
-
|
61
|
+
plugin_username = get_api_connector().get_plugin_username(plugin, debug=debug)
|
62
|
+
plugin_version = get_api_connector().get_plugin_version(plugin, debug=debug) or ' '
|
57
63
|
card_children = [
|
58
|
-
dbc.CardHeader(
|
64
|
+
dbc.CardHeader(
|
65
|
+
[
|
66
|
+
dbc.Row(
|
67
|
+
[
|
68
|
+
dbc.Col(html.A('👤 ' + str(plugin_username), href='#')),
|
69
|
+
dbc.Col(html.Pre(str(plugin_version), style={'text-align': 'right'})),
|
70
|
+
],
|
71
|
+
justify = 'between',
|
72
|
+
),
|
73
|
+
],
|
74
|
+
),
|
59
75
|
dbc.CardBody(card_body_children),
|
60
76
|
dbc.CardFooter([
|
61
|
-
html.A('⬇️ Download
|
77
|
+
html.A('⬇️ Download', href=(endpoints['plugins'] + '/' + plugin_name))
|
62
78
|
]),
|
63
79
|
]
|
64
80
|
cards.append(
|
meerschaum/api/routes/_login.py
CHANGED
@@ -39,10 +39,11 @@ def login(
|
|
39
39
|
else (data.username, data.password)
|
40
40
|
) if not no_auth else ('no-auth', 'no-auth')
|
41
41
|
|
42
|
-
from meerschaum.core.User._User import
|
42
|
+
from meerschaum.core.User._User import verify_password
|
43
43
|
user = User(username, password)
|
44
|
-
correct_password = no_auth or
|
45
|
-
password,
|
44
|
+
correct_password = no_auth or verify_password(
|
45
|
+
password,
|
46
|
+
get_api_connector().get_user_password_hash(user, debug=debug)
|
46
47
|
)
|
47
48
|
if not correct_password:
|
48
49
|
raise InvalidCredentialsException
|
@@ -51,7 +52,7 @@ def login(
|
|
51
52
|
expires_delta = timedelta(minutes=expires_minutes)
|
52
53
|
expires_dt = datetime.now(timezone.utc).replace(tzinfo=None) + expires_delta
|
53
54
|
access_token = manager.create_access_token(
|
54
|
-
data =
|
55
|
+
data = {'sub': username},
|
55
56
|
expires = expires_delta
|
56
57
|
)
|
57
58
|
return {
|
@@ -90,18 +90,21 @@ def register_plugin(
|
|
90
90
|
pass
|
91
91
|
|
92
92
|
plugin = Plugin(name, version=version, attributes=attributes)
|
93
|
+
if curr_user is None:
|
94
|
+
return (
|
95
|
+
False,
|
96
|
+
"Cannot register a plugin without logging in (are you running with `--insecure`)?"
|
97
|
+
)
|
98
|
+
|
93
99
|
if curr_user is not None:
|
94
100
|
plugin_user_id = get_api_connector().get_plugin_user_id(plugin)
|
95
101
|
curr_user_id = get_api_connector().get_user_id(curr_user) if curr_user is not None else -1
|
96
102
|
if plugin_user_id is not None and plugin_user_id != curr_user_id:
|
97
103
|
return False, f"User '{curr_user.username}' cannot edit plugin '{plugin}'."
|
98
104
|
plugin.user_id = curr_user_id
|
99
|
-
else:
|
100
|
-
plugin.user_id = -1
|
101
105
|
|
102
106
|
success, msg = get_api_connector().register_plugin(plugin, make_archive=False, debug=debug)
|
103
107
|
|
104
|
-
### TODO delete and install new version of plugin on success
|
105
108
|
if success:
|
106
109
|
archive_path = plugin.archive_path
|
107
110
|
temp_archive_path = pathlib.Path(str(archive_path) + '.tmp')
|
meerschaum/config/_default.py
CHANGED
@@ -44,6 +44,7 @@ default_meerschaum_config = {
|
|
44
44
|
},
|
45
45
|
'local': {
|
46
46
|
'host': 'localhost',
|
47
|
+
'port': 8000,
|
47
48
|
},
|
48
49
|
'mrsm': {
|
49
50
|
'host': 'api.mrsm.io',
|
@@ -151,7 +152,6 @@ default_config['pipes'] = default_pipes_config
|
|
151
152
|
default_config['plugins'] = default_plugins_config
|
152
153
|
from meerschaum.config._jobs import default_jobs_config
|
153
154
|
default_config['jobs'] = default_jobs_config
|
154
|
-
# default_config['experimental'] = default_experimental_config
|
155
155
|
### add configs from other packages
|
156
156
|
try:
|
157
157
|
import meerschaum.config.stack
|
@@ -160,6 +160,8 @@ except ImportError as e:
|
|
160
160
|
finally:
|
161
161
|
from meerschaum.config.stack import default_stack_config
|
162
162
|
default_config['stack'] = default_stack_config
|
163
|
+
from meerschaum.config._dash import default_dash_config
|
164
|
+
default_config['dash'] = default_dash_config
|
163
165
|
|
164
166
|
default_header_comment = """
|
165
167
|
#####################################################################
|
meerschaum/config/_jobs.py
CHANGED
@@ -9,13 +9,19 @@ Default configuration for jobs.
|
|
9
9
|
default_jobs_config = {
|
10
10
|
'timeout_seconds': 8,
|
11
11
|
'check_timeout_interval_seconds': 0.1,
|
12
|
-
'
|
12
|
+
'terminal': {
|
13
|
+
'lines': 40,
|
14
|
+
'columns': 70,
|
15
|
+
},
|
16
|
+
'logs': {
|
13
17
|
'num_files_to_keep': 5,
|
14
18
|
'max_file_size': 100_000,
|
15
19
|
'lines_to_show': 30,
|
16
|
-
'refresh_files_seconds': 5
|
17
|
-
'min_buffer_len':
|
18
|
-
'
|
20
|
+
'refresh_files_seconds': 5,
|
21
|
+
'min_buffer_len': 10,
|
22
|
+
'timestamp_format': '%Y-%m-%d %H:%M',
|
23
|
+
'follow_timestamp_format': '%H:%M',
|
24
|
+
'colors': [
|
19
25
|
'cyan',
|
20
26
|
'magenta',
|
21
27
|
'orange3',
|
meerschaum/config/_paths.py
CHANGED
meerschaum/config/_sync.py
CHANGED
@@ -52,7 +52,7 @@ def sync_yaml_configs(
|
|
52
52
|
if not path.exists():
|
53
53
|
return "", {}
|
54
54
|
header_comment = ""
|
55
|
-
with open(path, 'r') as f:
|
55
|
+
with open(path, 'r', encoding='utf-8') as f:
|
56
56
|
if _yaml is not None:
|
57
57
|
config = yaml.load(f)
|
58
58
|
else:
|
@@ -84,7 +84,7 @@ def sync_yaml_configs(
|
|
84
84
|
new_path = sub_path
|
85
85
|
|
86
86
|
### write changes
|
87
|
-
with open(new_path, 'w+') as f:
|
87
|
+
with open(new_path, 'w+', encoding='utf-8') as f:
|
88
88
|
f.write(new_header)
|
89
89
|
f.write(new_config_text)
|
90
90
|
if permissions is not None:
|
@@ -133,4 +133,3 @@ def sync_files(keys: Optional[List[str]] = None):
|
|
133
133
|
for k in keys:
|
134
134
|
if k in key_functions:
|
135
135
|
key_functions[k]()
|
136
|
-
|
meerschaum/config/_version.py
CHANGED
@@ -33,7 +33,7 @@ api_host = "api"
|
|
33
33
|
|
34
34
|
env_dict = {
|
35
35
|
'COMPOSE_PROJECT_NAME' : 'mrsm',
|
36
|
-
'TIMESCALEDB_VERSION' : 'latest-
|
36
|
+
'TIMESCALEDB_VERSION' : 'latest-pg16-oss',
|
37
37
|
'POSTGRES_USER' : f'{db_user}',
|
38
38
|
'POSTGRES_PASSWORD' : f'{db_pass}',
|
39
39
|
'POSTGRES_DB' : f'{db_base}',
|
@@ -232,11 +232,11 @@ NECESSARY_FILES = [STACK_COMPOSE_PATH, GRAFANA_DATASOURCE_PATH, GRAFANA_DASHBOAR
|
|
232
232
|
def get_necessary_files():
|
233
233
|
from meerschaum.config import get_config
|
234
234
|
return {
|
235
|
-
STACK_COMPOSE_PATH
|
235
|
+
STACK_COMPOSE_PATH: (
|
236
236
|
get_config('stack', STACK_COMPOSE_FILENAME, substitute=True), compose_header
|
237
237
|
),
|
238
|
-
GRAFANA_DATASOURCE_PATH
|
239
|
-
GRAFANA_DASHBOARD_PATH
|
238
|
+
GRAFANA_DATASOURCE_PATH: get_config('stack', 'grafana', 'datasource', substitute=True),
|
239
|
+
GRAFANA_DASHBOARD_PATH: get_config('stack', 'grafana', 'dashboard', substitute=True),
|
240
240
|
}
|
241
241
|
|
242
242
|
|
@@ -250,8 +250,8 @@ def write_stack(
|
|
250
250
|
return sync_files(['stack'])
|
251
251
|
|
252
252
|
def edit_stack(
|
253
|
-
action
|
254
|
-
debug
|
253
|
+
action: Optional[List[str]] = None,
|
254
|
+
debug: bool = False,
|
255
255
|
**kw
|
256
256
|
):
|
257
257
|
"""Open docker-compose.yaml or .env for editing."""
|
@@ -103,11 +103,13 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
103
103
|
},
|
104
104
|
'users': {
|
105
105
|
'password_hash': {
|
106
|
+
'algorithm_name': 'sha256',
|
107
|
+
'salt_bytes': 16,
|
106
108
|
'schemes': [
|
107
109
|
'pbkdf2_sha256',
|
108
110
|
],
|
109
111
|
'default': 'pbkdf2_sha256',
|
110
|
-
'pbkdf2_sha256__default_rounds':
|
112
|
+
'pbkdf2_sha256__default_rounds': 3_000_000,
|
111
113
|
},
|
112
114
|
'min_username_length': 1,
|
113
115
|
'max_username_length': 26,
|
@@ -108,9 +108,7 @@ def get_plugin_version(
|
|
108
108
|
plugins_tbl = get_tables(mrsm_instance=self, debug=debug)['plugins']
|
109
109
|
from meerschaum.utils.packages import attempt_import
|
110
110
|
sqlalchemy = attempt_import('sqlalchemy')
|
111
|
-
|
112
111
|
query = sqlalchemy.select(plugins_tbl.c.version).where(plugins_tbl.c.plugin_name == plugin.name)
|
113
|
-
|
114
112
|
return self.value(query, debug=debug)
|
115
113
|
|
116
114
|
def get_plugin_user_id(
|