meerschaum 2.2.0.dev3__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/_internal/entry.py +1 -1
- meerschaum/actions/show.py +128 -42
- 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 +63 -34
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +102 -0
- meerschaum/utils/daemon/RotatingFile.py +120 -14
- meerschaum/utils/daemon/__init__.py +1 -0
- meerschaum/utils/packages/__init__.py +9 -2
- meerschaum/utils/packages/_packages.py +3 -3
- meerschaum/utils/schedule.py +41 -47
- meerschaum/utils/threading.py +1 -0
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/METADATA +10 -9
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/RECORD +38 -36
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/WHEEL +1 -1
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.0.dev3.dist-info → meerschaum-2.2.0rc2.dist-info}/zip-safe +0 -0
meerschaum/__main__.py
CHANGED
meerschaum/_internal/entry.py
CHANGED
@@ -49,7 +49,7 @@ def entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
49
49
|
|
50
50
|
if args.get('schedule', None):
|
51
51
|
from meerschaum.utils.schedule import schedule_function
|
52
|
-
return schedule_function(entry_with_args,
|
52
|
+
return schedule_function(entry_with_args, **args)
|
53
53
|
return entry_with_args(**args)
|
54
54
|
|
55
55
|
|
meerschaum/actions/show.py
CHANGED
@@ -41,6 +41,7 @@ def show(
|
|
41
41
|
'jobs' : _show_jobs,
|
42
42
|
'logs' : _show_logs,
|
43
43
|
'tags' : _show_tags,
|
44
|
+
'schedules' : _show_schedules,
|
44
45
|
}
|
45
46
|
return choose_subaction(action, show_options, **kw)
|
46
47
|
|
@@ -577,6 +578,7 @@ def _show_logs(
|
|
577
578
|
`show logs myjob myotherjob`
|
578
579
|
"""
|
579
580
|
import os, pathlib, random, asyncio
|
581
|
+
from datetime import datetime, timezone
|
580
582
|
from meerschaum.utils.packages import attempt_import, import_rich
|
581
583
|
from meerschaum.utils.daemon import get_filtered_daemons, Daemon
|
582
584
|
from meerschaum.utils.warnings import warn, info
|
@@ -587,72 +589,106 @@ def _show_logs(
|
|
587
589
|
if not ANSI:
|
588
590
|
info = print
|
589
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')
|
590
594
|
daemons = get_filtered_daemons(action)
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
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
|
+
)
|
595
605
|
return {
|
596
|
-
d.daemon_id: ''.join([' ' for i in range(
|
606
|
+
d.daemon_id: ''.join([' ' for i in range(buffer_len - len(d.daemon_id))])
|
597
607
|
for d in daemons
|
598
608
|
}
|
599
609
|
|
600
|
-
def
|
610
|
+
def build_job_colors(daemons, _old_job_colors = None) -> Dict[str, str]:
|
601
611
|
return {d.daemon_id: colors[i % len(colors)] for i, d in enumerate(daemons)}
|
602
612
|
|
603
|
-
|
604
|
-
|
613
|
+
buffer_spaces = build_buffer_spaces(daemons)
|
614
|
+
job_colors = build_job_colors(daemons)
|
605
615
|
|
606
|
-
def
|
607
|
-
nonlocal
|
608
|
-
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:
|
609
619
|
d = Daemon(daemon_id=daemon_id)
|
610
620
|
if d not in daemons:
|
611
621
|
daemons = get_filtered_daemons(action)
|
612
|
-
|
613
|
-
return
|
622
|
+
buffer_spaces = build_buffer_spaces(daemons)
|
623
|
+
return buffer_spaces[daemon_id] or ' '
|
614
624
|
|
615
|
-
def
|
616
|
-
nonlocal
|
617
|
-
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:
|
618
628
|
d = Daemon(daemon_id=daemon_id)
|
619
629
|
if d not in daemons:
|
620
630
|
daemons = get_filtered_daemons(action)
|
621
|
-
|
622
|
-
return
|
631
|
+
job_colors = build_job_colors(daemons)
|
632
|
+
return job_colors[daemon_id]
|
623
633
|
|
624
|
-
def
|
625
|
-
|
634
|
+
def follow_pretty_print():
|
635
|
+
watchfiles = attempt_import('watchfiles')
|
626
636
|
rich = import_rich()
|
627
637
|
rich_text = attempt_import('rich.text')
|
628
|
-
|
638
|
+
watch_daemon_ids = {d.daemon_id: d for d in daemons}
|
629
639
|
info("Watching log files...")
|
630
640
|
|
631
|
-
|
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)]
|
645
|
+
try:
|
646
|
+
line_timestamp = datetime.strptime(date_prefix_str, timestamp_format)
|
647
|
+
previous_line_timestamp = line_timestamp
|
648
|
+
except Exception as e:
|
649
|
+
line_timestamp = None
|
650
|
+
if line_timestamp:
|
651
|
+
line = line[(len(now_str) + 3):]
|
652
|
+
else:
|
653
|
+
line_timestamp = previous_line_timestamp
|
654
|
+
|
655
|
+
if len(line) == 0 or line == '\n':
|
656
|
+
return
|
657
|
+
|
632
658
|
text = rich_text.Text(daemon.daemon_id)
|
633
|
-
|
634
|
-
|
635
|
-
+ (
|
659
|
+
line_prefix = (
|
660
|
+
get_buffer_spaces(daemon.daemon_id)
|
661
|
+
+ (line_timestamp.strftime(follow_timestamp_format) if line_timestamp else '')
|
662
|
+
+ ' | '
|
636
663
|
)
|
664
|
+
text.append(line_prefix + (line[:-1] if line[-1] == '\n' else line))
|
637
665
|
if ANSI:
|
638
666
|
text.stylize(
|
639
|
-
|
667
|
+
get_job_colors(daemon.daemon_id),
|
640
668
|
0,
|
641
|
-
len(daemon.daemon_id) + len(
|
669
|
+
len(daemon.daemon_id) + len(line_prefix)
|
642
670
|
)
|
643
671
|
get_console().print(text)
|
644
672
|
|
645
673
|
|
646
|
-
def
|
674
|
+
def print_log_lines(daemon):
|
647
675
|
for line in daemon.readlines():
|
648
|
-
|
676
|
+
print_job_line(daemon, line)
|
649
677
|
|
650
|
-
def
|
678
|
+
def seek_back_offset(d) -> bool:
|
651
679
|
if d.log_offset_path.exists():
|
652
680
|
d.log_offset_path.unlink()
|
653
681
|
|
654
682
|
latest_subfile_path = d.rotating_log.get_latest_subfile_path()
|
655
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
|
+
|
656
692
|
with open(latest_subfile_path, 'r', encoding='utf-8') as f:
|
657
693
|
latest_lines = f.readlines()
|
658
694
|
|
@@ -672,12 +708,12 @@ def _show_logs(
|
|
672
708
|
for d in daemons
|
673
709
|
}
|
674
710
|
for d in daemons:
|
675
|
-
|
676
|
-
|
711
|
+
seek_back_offset(d)
|
712
|
+
print_log_lines(d)
|
677
713
|
|
678
714
|
_quit = False
|
679
|
-
|
680
|
-
|
715
|
+
def watch_logs():
|
716
|
+
for changes in watchfiles.watch(LOGS_RESOURCES_PATH):
|
681
717
|
if _quit:
|
682
718
|
return
|
683
719
|
for change in changes:
|
@@ -688,7 +724,7 @@ def _show_logs(
|
|
688
724
|
if not file_path.exists():
|
689
725
|
continue
|
690
726
|
daemon_id = file_path.name.split('.log')[0]
|
691
|
-
if daemon_id not in
|
727
|
+
if daemon_id not in watch_daemon_ids and action:
|
692
728
|
continue
|
693
729
|
try:
|
694
730
|
daemon = Daemon(daemon_id=daemon_id)
|
@@ -697,22 +733,21 @@ def _show_logs(
|
|
697
733
|
warn(f"Seeing new logs for non-existent job '{daemon_id}'.", stack=False)
|
698
734
|
|
699
735
|
if daemon is not None:
|
700
|
-
|
736
|
+
print_log_lines(daemon)
|
701
737
|
|
702
|
-
loop = asyncio.new_event_loop()
|
703
738
|
try:
|
704
|
-
|
705
|
-
except KeyboardInterrupt:
|
739
|
+
watch_logs()
|
740
|
+
except KeyboardInterrupt as ki:
|
706
741
|
_quit = True
|
707
742
|
|
708
|
-
def
|
743
|
+
def print_nopretty_log_text():
|
709
744
|
for d in daemons:
|
710
745
|
log_text = d.log_text
|
711
746
|
print(d.daemon_id)
|
712
747
|
print(log_text)
|
713
748
|
|
714
|
-
|
715
|
-
|
749
|
+
print_log_text = follow_pretty_print if not nopretty else print_nopretty_log_text
|
750
|
+
print_log_text()
|
716
751
|
|
717
752
|
return True, "Success"
|
718
753
|
|
@@ -817,6 +852,57 @@ def _show_tags(
|
|
817
852
|
return True, "Success"
|
818
853
|
|
819
854
|
|
855
|
+
def _show_schedules(
|
856
|
+
action: Optional[List[str]] = None,
|
857
|
+
nopretty: bool = False,
|
858
|
+
**kwargs: Any
|
859
|
+
) -> SuccessTuple:
|
860
|
+
"""
|
861
|
+
Print the upcoming timestamps according to the given schedule.
|
862
|
+
|
863
|
+
Examples:
|
864
|
+
show schedule 'daily starting 00:00'
|
865
|
+
show schedule 'every 12 hours and mon-fri starting 2024-01-01'
|
866
|
+
"""
|
867
|
+
from meerschaum.utils.schedule import parse_schedule
|
868
|
+
from meerschaum.utils.misc import is_int
|
869
|
+
from meerschaum.utils.formatting import print_options
|
870
|
+
if not action:
|
871
|
+
return False, "Provide a schedule to be parsed."
|
872
|
+
schedule = action[0]
|
873
|
+
default_num_timestamps = 5
|
874
|
+
num_timestamps_str = action[1] if len(action) >= 2 else str(default_num_timestamps)
|
875
|
+
num_timestamps = (
|
876
|
+
int(num_timestamps_str)
|
877
|
+
if is_int(num_timestamps_str)
|
878
|
+
else default_num_timestamps
|
879
|
+
)
|
880
|
+
try:
|
881
|
+
trigger = parse_schedule(schedule)
|
882
|
+
except ValueError as e:
|
883
|
+
return False, str(e)
|
884
|
+
|
885
|
+
next_datetimes = []
|
886
|
+
for _ in range(num_timestamps):
|
887
|
+
try:
|
888
|
+
next_dt = trigger.next()
|
889
|
+
next_datetimes.append(next_dt)
|
890
|
+
except Exception as e:
|
891
|
+
break
|
892
|
+
|
893
|
+
print_options(
|
894
|
+
next_datetimes,
|
895
|
+
num_cols = 1,
|
896
|
+
nopretty = nopretty,
|
897
|
+
header = (
|
898
|
+
f"Next {min(num_timestamps, len(next_datetimes))} timestamps "
|
899
|
+
+ f"for schedule '{schedule}':"
|
900
|
+
),
|
901
|
+
)
|
902
|
+
|
903
|
+
return True, "Success"
|
904
|
+
|
905
|
+
|
820
906
|
|
821
907
|
### NOTE: This must be the final statement of the module.
|
822
908
|
### Any subactions added below these lines will not
|
@@ -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,
|