meerschaum 2.3.3__py3-none-any.whl → 2.3.5__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/_internal/arguments/__init__.py +2 -5
- meerschaum/_internal/arguments/_parse_arguments.py +117 -9
- meerschaum/_internal/docs/index.py +1 -1
- meerschaum/_internal/entry.py +40 -17
- meerschaum/actions/api.py +16 -16
- meerschaum/actions/python.py +4 -0
- meerschaum/actions/start.py +9 -5
- meerschaum/api/_events.py +11 -7
- meerschaum/api/routes/_actions.py +2 -90
- meerschaum/api/routes/_jobs.py +39 -19
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +2 -0
- meerschaum/connectors/api/_actions.py +22 -36
- meerschaum/connectors/api/_jobs.py +1 -0
- meerschaum/jobs/_Job.py +31 -8
- meerschaum/jobs/systemd.py +21 -9
- meerschaum/utils/prompt.py +1 -1
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/METADATA +1 -1
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/RECORD +25 -25
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/WHEEL +1 -1
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/LICENSE +0 -0
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/NOTICE +0 -0
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/top_level.txt +0 -0
- {meerschaum-2.3.3.dist-info → meerschaum-2.3.5.dist-info}/zip-safe +0 -0
@@ -9,11 +9,8 @@ This package includes argument parsing utilities.
|
|
9
9
|
from meerschaum._internal.arguments._parse_arguments import (
|
10
10
|
parse_arguments, parse_line, remove_leading_action,
|
11
11
|
parse_dict_to_sysargs, split_chained_sysargs, split_pipeline_sysargs,
|
12
|
+
sysargs_has_api_executor_keys, get_pipeline_sysargs,
|
13
|
+
compress_pipeline_sysargs, remove_api_executor_keys,
|
12
14
|
)
|
13
15
|
from meerschaum._internal.arguments._parser import parser
|
14
16
|
from meerschaum.plugins import add_plugin_argument
|
15
|
-
|
16
|
-
__all__ = [
|
17
|
-
'parser', 'parse_arguments', 'parse_line', 'add_plugin_argument', 'parse_dict_to_sysargs',
|
18
|
-
'remove_leading_action',
|
19
|
-
]
|
@@ -283,18 +283,29 @@ def parse_dict_to_sysargs(
|
|
283
283
|
### Add list flags
|
284
284
|
if isinstance(args_dict[a], (list, tuple)):
|
285
285
|
if len(args_dict[a]) > 0:
|
286
|
-
|
287
|
-
[
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
286
|
+
if a == 'sub_args' and args_dict[a] != ['']:
|
287
|
+
print(f"{args_dict[a]=}")
|
288
|
+
sysargs.extend(
|
289
|
+
[
|
290
|
+
'-A',
|
291
|
+
shlex.join([
|
292
|
+
str(item) for item in args_dict[a]
|
293
|
+
]),
|
294
|
+
]
|
295
|
+
)
|
296
|
+
else:
|
297
|
+
sysargs.extend(
|
298
|
+
[t[0]]
|
299
|
+
+ [
|
300
|
+
str(item)
|
301
|
+
for item in args_dict[a]
|
302
|
+
]
|
303
|
+
)
|
293
304
|
|
294
305
|
### Add dict flags
|
295
306
|
elif isinstance(args_dict[a], dict):
|
296
307
|
if len(args_dict[a]) > 0:
|
297
|
-
sysargs += [t[0], json.dumps(args_dict[a])]
|
308
|
+
sysargs += [t[0], json.dumps(args_dict[a], separators=(',', ':'))]
|
298
309
|
|
299
310
|
### Account for None and other values
|
300
311
|
elif (args_dict[a] is not None) or (args_dict[a] is None and a in allow_none_args):
|
@@ -403,4 +414,101 @@ def load_plugin_args() -> None:
|
|
403
414
|
to_import.append(plugin.name)
|
404
415
|
if not to_import:
|
405
416
|
return
|
406
|
-
import_plugins(*to_import)
|
417
|
+
import_plugins(*to_import)
|
418
|
+
|
419
|
+
|
420
|
+
def sysargs_has_api_executor_keys(sysargs: List[str]) -> bool:
|
421
|
+
"""
|
422
|
+
Check whether a `sysargs` list contains an `api` executor.
|
423
|
+
"""
|
424
|
+
if '-e' not in sysargs and '--executor-keys' not in sysargs:
|
425
|
+
return False
|
426
|
+
|
427
|
+
for i, arg in enumerate(sysargs):
|
428
|
+
if arg not in ('-e', '--executor-keys'):
|
429
|
+
continue
|
430
|
+
|
431
|
+
executor_keys_ix = i + 1
|
432
|
+
if len(sysargs) <= executor_keys_ix:
|
433
|
+
return False
|
434
|
+
|
435
|
+
executor_keys = sysargs[executor_keys_ix]
|
436
|
+
if executor_keys.startswith('api:'):
|
437
|
+
return True
|
438
|
+
|
439
|
+
return False
|
440
|
+
|
441
|
+
|
442
|
+
def remove_api_executor_keys(sysargs: List[str]) -> List[str]:
|
443
|
+
"""
|
444
|
+
Remove any api executor keys from `sysargs`.
|
445
|
+
"""
|
446
|
+
from meerschaum.utils.misc import flatten_list
|
447
|
+
|
448
|
+
if not sysargs_has_api_executor_keys(sysargs):
|
449
|
+
return sysargs
|
450
|
+
|
451
|
+
skip_indices = set(flatten_list(
|
452
|
+
[
|
453
|
+
[i, i+1]
|
454
|
+
for i, arg in enumerate(sysargs)
|
455
|
+
if arg in ('-e', '--executor-keys')
|
456
|
+
]
|
457
|
+
))
|
458
|
+
|
459
|
+
return [
|
460
|
+
arg
|
461
|
+
for i, arg in enumerate(sysargs)
|
462
|
+
if i not in skip_indices
|
463
|
+
]
|
464
|
+
|
465
|
+
|
466
|
+
def get_pipeline_sysargs(
|
467
|
+
sysargs: List[str],
|
468
|
+
pipeline_args: List[str],
|
469
|
+
_patch_args: Optional[Dict[str, Any]] = None,
|
470
|
+
) -> List[str]:
|
471
|
+
"""
|
472
|
+
Parse `sysargs` and `pipeline_args` into a single `start pipeline` sysargs.
|
473
|
+
"""
|
474
|
+
import shlex
|
475
|
+
start_pipeline_params = {
|
476
|
+
'sub_args_line': shlex.join(sysargs),
|
477
|
+
'patch_args': _patch_args,
|
478
|
+
}
|
479
|
+
return (
|
480
|
+
['start', 'pipeline']
|
481
|
+
+ [str(arg) for arg in pipeline_args]
|
482
|
+
+ ['-P', json.dumps(start_pipeline_params, separators=(',', ':'))]
|
483
|
+
)
|
484
|
+
|
485
|
+
|
486
|
+
def compress_pipeline_sysargs(pipeline_sysargs: List[str]) -> List[str]:
|
487
|
+
"""
|
488
|
+
Given a `start pipeline` sysargs, return a condensed syntax rendition.
|
489
|
+
"""
|
490
|
+
import shlex
|
491
|
+
|
492
|
+
if pipeline_sysargs[:2] != ['start', 'pipeline']:
|
493
|
+
return pipeline_sysargs
|
494
|
+
|
495
|
+
if '-P' not in pipeline_sysargs:
|
496
|
+
return pipeline_sysargs
|
497
|
+
|
498
|
+
params_ix = pipeline_sysargs.index('-P')
|
499
|
+
pipeline_args = pipeline_sysargs[2:params_ix]
|
500
|
+
params_str = pipeline_sysargs[-1]
|
501
|
+
try:
|
502
|
+
start_pipeline_params = json.loads(params_str)
|
503
|
+
except Exception:
|
504
|
+
return pipeline_sysargs
|
505
|
+
|
506
|
+
sub_args_line = start_pipeline_params.get('sub_args_line', None)
|
507
|
+
if not sub_args_line:
|
508
|
+
return pipeline_sysargs
|
509
|
+
|
510
|
+
return (
|
511
|
+
shlex.split(sub_args_line)
|
512
|
+
+ [':']
|
513
|
+
+ pipeline_args
|
514
|
+
)
|
meerschaum/_internal/entry.py
CHANGED
@@ -11,6 +11,8 @@ from __future__ import annotations
|
|
11
11
|
|
12
12
|
import os
|
13
13
|
import sys
|
14
|
+
import pathlib
|
15
|
+
|
14
16
|
from meerschaum.utils.typing import SuccessTuple, List, Optional, Dict, Callable, Any
|
15
17
|
from meerschaum.config.static import STATIC_CONFIG as _STATIC_CONFIG
|
16
18
|
|
@@ -19,8 +21,17 @@ if (_STATIC_CONFIG['environment']['systemd_log_path']) in os.environ:
|
|
19
21
|
from meerschaum.utils.daemon import RotatingFile as _RotatingFile, StdinFile as _StdinFile
|
20
22
|
from meerschaum.config import get_config as _get_config
|
21
23
|
|
22
|
-
_systemd_result_path =
|
23
|
-
|
24
|
+
_systemd_result_path = pathlib.Path(
|
25
|
+
os.environ[_STATIC_CONFIG['environment']['systemd_result_path']]
|
26
|
+
)
|
27
|
+
_systemd_log_path = pathlib.Path(
|
28
|
+
os.environ[_STATIC_CONFIG['environment']['systemd_log_path']]
|
29
|
+
)
|
30
|
+
_systemd_delete_job = (
|
31
|
+
(os.environ.get(_STATIC_CONFIG['environment']['systemd_delete_job'], None) or '0')
|
32
|
+
not in (None, '0', 'false')
|
33
|
+
)
|
34
|
+
_job_name = os.environ[_STATIC_CONFIG['environment']['daemon_id']]
|
24
35
|
_systemd_log = _RotatingFile(
|
25
36
|
_systemd_log_path,
|
26
37
|
write_timestamps=True,
|
@@ -50,6 +61,8 @@ def entry(
|
|
50
61
|
parse_arguments,
|
51
62
|
split_chained_sysargs,
|
52
63
|
split_pipeline_sysargs,
|
64
|
+
sysargs_has_api_executor_keys,
|
65
|
+
get_pipeline_sysargs,
|
53
66
|
)
|
54
67
|
from meerschaum.config.static import STATIC_CONFIG
|
55
68
|
if sysargs is None:
|
@@ -63,22 +76,15 @@ def entry(
|
|
63
76
|
|
64
77
|
has_daemon = '-d' in sysargs or '--daemon' in sysargs
|
65
78
|
has_start_job = sysargs[:2] == ['start', 'job']
|
79
|
+
pipeline_has_api_executor_keys = sysargs_has_api_executor_keys(pipeline_args)
|
80
|
+
|
66
81
|
chained_sysargs = (
|
67
82
|
[sysargs]
|
68
|
-
if has_daemon or has_start_job
|
83
|
+
if has_daemon or has_start_job or pipeline_has_api_executor_keys
|
69
84
|
else split_chained_sysargs(sysargs)
|
70
85
|
)
|
71
86
|
if pipeline_args:
|
72
|
-
chained_sysargs = [
|
73
|
-
['start', 'pipeline']
|
74
|
-
+ [str(arg) for arg in pipeline_args]
|
75
|
-
+ (
|
76
|
-
['--params', json.dumps(_patch_args)]
|
77
|
-
if _patch_args
|
78
|
-
else []
|
79
|
-
)
|
80
|
-
+ ['--sub-args', shlex.join(sysargs)]
|
81
|
-
]
|
87
|
+
chained_sysargs = [get_pipeline_sysargs(sysargs, pipeline_args, _patch_args=_patch_args)]
|
82
88
|
|
83
89
|
results: List[SuccessTuple] = []
|
84
90
|
|
@@ -127,7 +133,12 @@ def entry(
|
|
127
133
|
(
|
128
134
|
'Successfully c'
|
129
135
|
if success
|
130
|
-
else
|
136
|
+
else (
|
137
|
+
'Failed pipeline after '
|
138
|
+
+ f"{len(success_messages)} step"
|
139
|
+
+ ('s' if len(success_messages) != 1 else '')
|
140
|
+
+ '.\n\nC'
|
141
|
+
)
|
131
142
|
) + 'ompleted step'
|
132
143
|
+ ('s' if len(success_messages) != 1 else '')
|
133
144
|
+ ':\n\n'
|
@@ -158,12 +169,24 @@ def entry(
|
|
158
169
|
successes_msg
|
159
170
|
+ ('\n\n' if any_success else '')
|
160
171
|
+ fails_msg
|
161
|
-
).rstrip()
|
172
|
+
).rstrip() if len(chained_sysargs) > 1 else results[0][1]
|
162
173
|
|
163
174
|
if _systemd_result_path:
|
164
175
|
import json
|
165
|
-
|
166
|
-
|
176
|
+
from meerschaum.utils.warnings import warn
|
177
|
+
import meerschaum as mrsm
|
178
|
+
|
179
|
+
job = mrsm.Job(_job_name, executor_keys='systemd')
|
180
|
+
if job.delete_after_completion:
|
181
|
+
delete_success, delete_msg = job.delete()
|
182
|
+
mrsm.pprint((delete_success, delete_msg))
|
183
|
+
else:
|
184
|
+
try:
|
185
|
+
if _systemd_result_path.parent.exists():
|
186
|
+
with open(_systemd_result_path, 'w+', encoding='utf-8') as f:
|
187
|
+
json.dump((success, msg), f)
|
188
|
+
except Exception as e:
|
189
|
+
warn(f"Failed to write job result:\n{e}")
|
167
190
|
|
168
191
|
return success, msg
|
169
192
|
|
meerschaum/actions/api.py
CHANGED
@@ -89,22 +89,22 @@ def api(
|
|
89
89
|
return success, message
|
90
90
|
|
91
91
|
def _api_start(
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
92
|
+
action: Optional[List[str]] = None,
|
93
|
+
host: Optional[str] = None,
|
94
|
+
port: Optional[int] = None,
|
95
|
+
workers: Optional[int] = None,
|
96
|
+
mrsm_instance: Optional[str] = None,
|
97
|
+
no_dash: bool = False,
|
98
|
+
no_auth: bool = False,
|
99
|
+
private: bool = False,
|
100
|
+
secure: bool = False,
|
101
|
+
debug: bool = False,
|
102
|
+
nopretty: bool = False,
|
103
|
+
production: bool = False,
|
104
|
+
keyfile: Optional[str] = None,
|
105
|
+
certfile: Optional[str] = None,
|
106
|
+
**kw: Any
|
107
|
+
) -> SuccessTuple:
|
108
108
|
"""Start the API server.
|
109
109
|
|
110
110
|
Parameters
|
meerschaum/actions/python.py
CHANGED
@@ -14,6 +14,7 @@ def python(
|
|
14
14
|
nopretty: bool = False,
|
15
15
|
noask: bool = False,
|
16
16
|
venv: Optional[str] = None,
|
17
|
+
executor_keys: Optional[str] = None,
|
17
18
|
debug: bool = False,
|
18
19
|
**kw: Any
|
19
20
|
) -> SuccessTuple:
|
@@ -53,6 +54,9 @@ def python(
|
|
53
54
|
from meerschaum.utils.packages import run_python_package, attempt_import
|
54
55
|
from meerschaum.utils.process import run_process
|
55
56
|
|
57
|
+
if executor_keys and executor_keys.startswith('api:'):
|
58
|
+
warn("Cannot open a Python REPL remotely, falling back to local...", stack=False)
|
59
|
+
|
56
60
|
if action is None:
|
57
61
|
action = []
|
58
62
|
|
meerschaum/actions/start.py
CHANGED
@@ -7,7 +7,7 @@ Start subsystems (API server, logging daemon, etc.).
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
from meerschaum.utils.typing import SuccessTuple, Optional, List, Any, Union
|
10
|
+
from meerschaum.utils.typing import SuccessTuple, Optional, List, Any, Union, Dict
|
11
11
|
|
12
12
|
def start(
|
13
13
|
action: Optional[List[str]] = None,
|
@@ -89,6 +89,7 @@ def _start_jobs(
|
|
89
89
|
name: Optional[str] = None,
|
90
90
|
sysargs: Optional[List[str]] = None,
|
91
91
|
executor_keys: Optional[str] = None,
|
92
|
+
rm: bool = False,
|
92
93
|
debug: bool = False,
|
93
94
|
**kw
|
94
95
|
) -> SuccessTuple:
|
@@ -210,7 +211,7 @@ def _start_jobs(
|
|
210
211
|
|
211
212
|
def _run_new_job(name: Optional[str] = None):
|
212
213
|
name = name or get_new_daemon_name()
|
213
|
-
job = Job(name, sysargs, executor_keys=executor_keys)
|
214
|
+
job = Job(name, sysargs, executor_keys=executor_keys, delete_after_completion=rm)
|
214
215
|
return job.start(debug=debug), name
|
215
216
|
|
216
217
|
def _run_existing_job(name: str):
|
@@ -539,7 +540,6 @@ def _complete_start_connectors(**kw) -> List[str]:
|
|
539
540
|
|
540
541
|
def _start_pipeline(
|
541
542
|
action: Optional[List[str]] = None,
|
542
|
-
sub_args: Optional[List[str]] = None,
|
543
543
|
loop: bool = False,
|
544
544
|
min_seconds: Union[float, int, None] = 1.0,
|
545
545
|
params: Optional[Dict[str, Any]] = None,
|
@@ -569,7 +569,11 @@ def _start_pipeline(
|
|
569
569
|
else 1
|
570
570
|
)
|
571
571
|
|
572
|
-
|
572
|
+
params = params or {}
|
573
|
+
sub_args_line = params.get('sub_args_line', None)
|
574
|
+
patch_args = params.get('patch_args', None)
|
575
|
+
|
576
|
+
if not sub_args_line:
|
573
577
|
return False, "Nothing to do."
|
574
578
|
|
575
579
|
if min_seconds is None:
|
@@ -580,7 +584,7 @@ def _start_pipeline(
|
|
580
584
|
def run_loop():
|
581
585
|
nonlocal ran_n_times, success, msg
|
582
586
|
while True:
|
583
|
-
success, msg = entry(
|
587
|
+
success, msg = entry(sub_args_line, _patch_args=patch_args)
|
584
588
|
ran_n_times += 1
|
585
589
|
|
586
590
|
if not loop and do_n_times == 1:
|
meerschaum/api/_events.py
CHANGED
@@ -20,12 +20,14 @@ from meerschaum.connectors.poll import retry_connect
|
|
20
20
|
from meerschaum.utils.warnings import warn
|
21
21
|
from meerschaum._internal.term.tools import is_webterm_running
|
22
22
|
from meerschaum.jobs import (
|
23
|
+
get_jobs,
|
23
24
|
start_check_jobs_thread,
|
24
25
|
stop_check_jobs_thread,
|
25
26
|
get_executor_keys_from_context,
|
26
27
|
)
|
28
|
+
from meerschaum.config.static import STATIC_CONFIG
|
27
29
|
|
28
|
-
|
30
|
+
TEMP_PREFIX: str = STATIC_CONFIG['api']['jobs']['temp_prefix']
|
29
31
|
|
30
32
|
@app.on_event("startup")
|
31
33
|
async def startup():
|
@@ -51,8 +53,7 @@ async def startup():
|
|
51
53
|
await shutdown()
|
52
54
|
os._exit(1)
|
53
55
|
|
54
|
-
|
55
|
-
start_check_jobs_thread()
|
56
|
+
start_check_jobs_thread()
|
56
57
|
|
57
58
|
|
58
59
|
@app.on_event("shutdown")
|
@@ -65,11 +66,14 @@ async def shutdown():
|
|
65
66
|
if get_api_connector().type == 'sql':
|
66
67
|
get_api_connector().engine.dispose()
|
67
68
|
|
68
|
-
|
69
|
-
stop_check_jobs_thread()
|
69
|
+
stop_check_jobs_thread()
|
70
70
|
|
71
|
-
|
72
|
-
|
71
|
+
temp_jobs = {
|
72
|
+
name: job
|
73
|
+
for name, job in get_jobs(include_hidden=True).items()
|
74
|
+
if name.startswith(TEMP_PREFIX)
|
75
|
+
}
|
76
|
+
for job in temp_jobs.values():
|
73
77
|
job.delete()
|
74
78
|
|
75
79
|
### Terminate any running jobs left over.
|
@@ -14,6 +14,7 @@ from functools import partial
|
|
14
14
|
from datetime import datetime, timezone
|
15
15
|
|
16
16
|
from fastapi import WebSocket, WebSocketDisconnect
|
17
|
+
from websockets.exceptions import ConnectionClosedError
|
17
18
|
|
18
19
|
from meerschaum.utils.misc import generate_password
|
19
20
|
from meerschaum.jobs import Job
|
@@ -27,6 +28,7 @@ import meerschaum.core
|
|
27
28
|
from meerschaum.config import get_config
|
28
29
|
from meerschaum._internal.arguments._parse_arguments import parse_dict_to_sysargs, parse_arguments
|
29
30
|
from meerschaum.api.routes._jobs import clean_sysargs
|
31
|
+
from meerschaum.jobs._Job import StopMonitoringLogs
|
30
32
|
|
31
33
|
actions_endpoint = endpoints['actions']
|
32
34
|
|
@@ -65,96 +67,6 @@ def get_actions(
|
|
65
67
|
return list(actions)
|
66
68
|
|
67
69
|
|
68
|
-
async def notify_client(client, content: str):
|
69
|
-
"""
|
70
|
-
Send a line of text to a client.
|
71
|
-
"""
|
72
|
-
try:
|
73
|
-
await client.send_text(content)
|
74
|
-
except WebSocketDisconnect:
|
75
|
-
pass
|
76
|
-
|
77
|
-
_temp_jobs = {}
|
78
|
-
@app.websocket(actions_endpoint + '/ws')
|
79
|
-
async def do_action_websocket(websocket: WebSocket):
|
80
|
-
"""
|
81
|
-
Execute an action and stream the output to the client.
|
82
|
-
"""
|
83
|
-
await websocket.accept()
|
84
|
-
|
85
|
-
stop_event = asyncio.Event()
|
86
|
-
|
87
|
-
async def monitor_logs(job):
|
88
|
-
success, msg = job.start()
|
89
|
-
await job.monitor_logs_async(
|
90
|
-
partial(notify_client, websocket),
|
91
|
-
stop_event=stop_event,
|
92
|
-
stop_on_exit=True,
|
93
|
-
)
|
94
|
-
|
95
|
-
job = None
|
96
|
-
job_name = '.' + generate_password(12)
|
97
|
-
try:
|
98
|
-
token = await websocket.receive_text()
|
99
|
-
user = await manager.get_current_user(token) if not no_auth else None
|
100
|
-
if user is None and not no_auth:
|
101
|
-
raise fastapi.HTTPException(
|
102
|
-
status_code=401,
|
103
|
-
detail="Invalid credentials.",
|
104
|
-
)
|
105
|
-
|
106
|
-
auth_success, auth_msg = (
|
107
|
-
is_user_allowed_to_execute(user)
|
108
|
-
if not no_auth
|
109
|
-
else (True, "Success")
|
110
|
-
)
|
111
|
-
auth_payload = {
|
112
|
-
'is_authenticated': auth_success,
|
113
|
-
'timestamp': datetime.now(timezone.utc).isoformat(),
|
114
|
-
}
|
115
|
-
await websocket.send_json(auth_payload)
|
116
|
-
if not auth_success:
|
117
|
-
await websocket.close()
|
118
|
-
|
119
|
-
sysargs = clean_sysargs(await websocket.receive_json())
|
120
|
-
# kwargs = parse_arguments(sysargs)
|
121
|
-
# _ = kwargs.pop('executor_keys', None)
|
122
|
-
# _ = kwargs.pop('shell', None)
|
123
|
-
# sysargs = parse_dict_to_sysargs(kwargs)
|
124
|
-
|
125
|
-
job = Job(
|
126
|
-
job_name,
|
127
|
-
sysargs,
|
128
|
-
executor_keys='local',
|
129
|
-
_properties={
|
130
|
-
'logs': {
|
131
|
-
'write_timestamps': False,
|
132
|
-
},
|
133
|
-
},
|
134
|
-
)
|
135
|
-
_temp_jobs[job_name] = job
|
136
|
-
monitor_task = asyncio.create_task(monitor_logs(job))
|
137
|
-
await monitor_task
|
138
|
-
try:
|
139
|
-
await websocket.close()
|
140
|
-
except RuntimeError:
|
141
|
-
pass
|
142
|
-
except fastapi.HTTPException:
|
143
|
-
await websocket.send_text("Invalid credentials.")
|
144
|
-
await websocket.close()
|
145
|
-
except WebSocketDisconnect:
|
146
|
-
pass
|
147
|
-
except asyncio.CancelledError:
|
148
|
-
pass
|
149
|
-
except Exception:
|
150
|
-
warn(f"Error in logs websocket:\n{traceback.format_exc()}")
|
151
|
-
finally:
|
152
|
-
if job is not None:
|
153
|
-
job.delete()
|
154
|
-
_ = _temp_jobs.pop(job_name, None)
|
155
|
-
stop_event.set()
|
156
|
-
|
157
|
-
|
158
70
|
@app.post(actions_endpoint + "/{action}", tags=['Actions'])
|
159
71
|
def do_action_legacy(
|
160
72
|
action: str,
|
meerschaum/api/routes/_jobs.py
CHANGED
@@ -40,7 +40,15 @@ from meerschaum.config.static import STATIC_CONFIG
|
|
40
40
|
|
41
41
|
JOBS_STDIN_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stdin_message']
|
42
42
|
JOBS_STOP_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stop_message']
|
43
|
-
EXECUTOR_KEYS: str =
|
43
|
+
EXECUTOR_KEYS: str = 'local'
|
44
|
+
|
45
|
+
|
46
|
+
def _get_job(name: str):
|
47
|
+
systemd_job = Job(name, executor_keys='systemd')
|
48
|
+
if systemd_job.exists():
|
49
|
+
return systemd_job
|
50
|
+
|
51
|
+
return Job(name, executor_keys=EXECUTOR_KEYS)
|
44
52
|
|
45
53
|
|
46
54
|
@app.get(endpoints['jobs'], tags=['Jobs'])
|
@@ -52,7 +60,7 @@ def get_jobs(
|
|
52
60
|
"""
|
53
61
|
Return metadata about the current jobs.
|
54
62
|
"""
|
55
|
-
jobs = _get_jobs(executor_keys=EXECUTOR_KEYS, combine_local_and_systemd=
|
63
|
+
jobs = _get_jobs(executor_keys=EXECUTOR_KEYS, combine_local_and_systemd=True)
|
56
64
|
return {
|
57
65
|
name: {
|
58
66
|
'sysargs': job.sysargs,
|
@@ -83,7 +91,7 @@ def get_job(
|
|
83
91
|
"""
|
84
92
|
Return metadata for a single job.
|
85
93
|
"""
|
86
|
-
job =
|
94
|
+
job = _get_job(name)
|
87
95
|
if not job.exists():
|
88
96
|
raise fastapi.HTTPException(
|
89
97
|
status_code=404,
|
@@ -114,7 +122,7 @@ def clean_sysargs(sysargs: List[str]) -> List[str]:
|
|
114
122
|
clean_sysargs = []
|
115
123
|
executor_flag = False
|
116
124
|
for arg in sysargs:
|
117
|
-
if arg in ('-e', '--executor', 'api'):
|
125
|
+
if arg in ('-e', '--executor-keys', 'api'):
|
118
126
|
executor_flag = True
|
119
127
|
continue
|
120
128
|
if executor_flag:
|
@@ -163,7 +171,7 @@ def delete_job(
|
|
163
171
|
"""
|
164
172
|
Delete a job.
|
165
173
|
"""
|
166
|
-
job =
|
174
|
+
job = _get_job(name)
|
167
175
|
return job.delete()
|
168
176
|
|
169
177
|
|
@@ -177,7 +185,7 @@ def get_job_exists(
|
|
177
185
|
"""
|
178
186
|
Return whether a job exists.
|
179
187
|
"""
|
180
|
-
job =
|
188
|
+
job = _get_job(name)
|
181
189
|
return job.exists()
|
182
190
|
|
183
191
|
|
@@ -192,7 +200,7 @@ def get_logs(
|
|
192
200
|
Return a job's log text.
|
193
201
|
To stream log text, connect to the WebSocket endpoint `/logs/{name}/ws`.
|
194
202
|
"""
|
195
|
-
job =
|
203
|
+
job = _get_job(name)
|
196
204
|
if not job.exists():
|
197
205
|
raise fastapi.HTTPException(
|
198
206
|
status_code=404,
|
@@ -212,7 +220,7 @@ def start_job(
|
|
212
220
|
"""
|
213
221
|
Start a job if stopped.
|
214
222
|
"""
|
215
|
-
job =
|
223
|
+
job = _get_job(name)
|
216
224
|
if not job.exists():
|
217
225
|
raise fastapi.HTTPException(
|
218
226
|
status_code=404,
|
@@ -231,7 +239,7 @@ def stop_job(
|
|
231
239
|
"""
|
232
240
|
Stop a job if running.
|
233
241
|
"""
|
234
|
-
job =
|
242
|
+
job = _get_job(name)
|
235
243
|
if not job.exists():
|
236
244
|
raise fastapi.HTTPException(
|
237
245
|
status_code=404,
|
@@ -250,7 +258,7 @@ def pause_job(
|
|
250
258
|
"""
|
251
259
|
Pause a job if running.
|
252
260
|
"""
|
253
|
-
job =
|
261
|
+
job = _get_job(name)
|
254
262
|
if not job.exists():
|
255
263
|
raise fastapi.HTTPException(
|
256
264
|
status_code=404,
|
@@ -269,7 +277,7 @@ def get_stop_time(
|
|
269
277
|
"""
|
270
278
|
Get the timestamp when the job was manually stopped.
|
271
279
|
"""
|
272
|
-
job =
|
280
|
+
job = _get_job(name)
|
273
281
|
return job.stop_time
|
274
282
|
|
275
283
|
|
@@ -283,12 +291,13 @@ def get_is_blocking_on_stdin(
|
|
283
291
|
"""
|
284
292
|
Return whether a job is blocking on stdin.
|
285
293
|
"""
|
286
|
-
job =
|
294
|
+
job = _get_job(name)
|
287
295
|
return job.is_blocking_on_stdin()
|
288
296
|
|
289
297
|
|
290
298
|
_job_clients = defaultdict(lambda: [])
|
291
299
|
_job_stop_events = defaultdict(lambda: asyncio.Event())
|
300
|
+
_job_queues = defaultdict(lambda: asyncio.Queue())
|
292
301
|
async def notify_clients(name: str, websocket: WebSocket, content: str):
|
293
302
|
"""
|
294
303
|
Write the given content to all connected clients.
|
@@ -315,12 +324,16 @@ async def get_input_from_clients(name: str, websocket: WebSocket) -> str:
|
|
315
324
|
async def _read_client(client):
|
316
325
|
try:
|
317
326
|
await client.send_text(JOBS_STDIN_MESSAGE)
|
318
|
-
data = await
|
327
|
+
data = await _job_queues[name].get()
|
319
328
|
except WebSocketDisconnect:
|
320
329
|
if client in _job_clients[name]:
|
321
330
|
_job_clients[name].remove(client)
|
331
|
+
if not _job_clients[name]:
|
332
|
+
_job_stop_events[name].set()
|
322
333
|
except Exception:
|
323
334
|
pass
|
335
|
+
finally:
|
336
|
+
_job_queues[name].task_done()
|
324
337
|
return data
|
325
338
|
|
326
339
|
read_tasks = [
|
@@ -357,10 +370,12 @@ async def logs_websocket(name: str, websocket: WebSocket):
|
|
357
370
|
Stream logs from a job over a websocket.
|
358
371
|
"""
|
359
372
|
await websocket.accept()
|
360
|
-
job =
|
373
|
+
job = _get_job(name)
|
361
374
|
_job_clients[name].append(websocket)
|
362
375
|
|
376
|
+
_task = None
|
363
377
|
async def monitor_logs():
|
378
|
+
nonlocal _task
|
364
379
|
try:
|
365
380
|
callback_function = partial(
|
366
381
|
notify_clients,
|
@@ -377,16 +392,17 @@ async def logs_websocket(name: str, websocket: WebSocket):
|
|
377
392
|
name,
|
378
393
|
websocket,
|
379
394
|
)
|
380
|
-
|
395
|
+
_task = asyncio.create_task(job.monitor_logs_async(
|
381
396
|
callback_function=callback_function,
|
382
397
|
input_callback_function=input_callback_function,
|
383
398
|
stop_callback_function=stop_callback_function,
|
384
399
|
stop_event=_job_stop_events[name],
|
385
400
|
stop_on_exit=True,
|
386
401
|
accept_input=True,
|
387
|
-
)
|
402
|
+
))
|
388
403
|
except Exception:
|
389
404
|
warn(traceback.format_exc())
|
405
|
+
_task.cancel()
|
390
406
|
|
391
407
|
try:
|
392
408
|
token = await websocket.receive_text()
|
@@ -397,13 +413,17 @@ async def logs_websocket(name: str, websocket: WebSocket):
|
|
397
413
|
detail="Invalid credentials.",
|
398
414
|
)
|
399
415
|
monitor_task = asyncio.create_task(monitor_logs())
|
400
|
-
|
416
|
+
while True:
|
417
|
+
text = await websocket.receive_text()
|
418
|
+
await _job_queues[name].put(text)
|
419
|
+
|
401
420
|
except fastapi.HTTPException:
|
402
421
|
await websocket.send_text("Invalid credentials.")
|
403
422
|
await websocket.close()
|
404
423
|
except WebSocketDisconnect:
|
405
|
-
|
406
|
-
|
424
|
+
if not _job_clients[name]:
|
425
|
+
_job_stop_events[name].set()
|
426
|
+
monitor_task.cancel()
|
407
427
|
except asyncio.CancelledError:
|
408
428
|
pass
|
409
429
|
except Exception:
|
meerschaum/config/_version.py
CHANGED
@@ -46,6 +46,7 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
46
46
|
'stdin_message': 'MRSM_STDIN',
|
47
47
|
'stop_message': 'MRSM_STOP',
|
48
48
|
'metadata_cache_seconds': 5,
|
49
|
+
'temp_prefix': '.api-temp-',
|
49
50
|
},
|
50
51
|
},
|
51
52
|
'sql': {
|
@@ -73,6 +74,7 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
73
74
|
'systemd_log_path': 'MRSM_SYSTEMD_LOG_PATH',
|
74
75
|
'systemd_stdin_path': 'MRSM_SYSTEMD_STDIN_PATH',
|
75
76
|
'systemd_result_path': 'MRSM_SYSTEMD_RESULT_PATH',
|
77
|
+
'systemd_delete_job': 'MRSM_SYSTEMD_DELETE_JOB',
|
76
78
|
'uri_regex': r'MRSM_([a-zA-Z0-9]*)_(\d*[a-zA-Z][a-zA-Z0-9-_+]*$)',
|
77
79
|
'prefix': 'MRSM_',
|
78
80
|
},
|
@@ -17,6 +17,7 @@ from meerschaum.utils.typing import SuccessTuple, List, Callable, Optional
|
|
17
17
|
from meerschaum.config.static import STATIC_CONFIG
|
18
18
|
|
19
19
|
ACTIONS_ENDPOINT: str = STATIC_CONFIG['api']['endpoints']['actions']
|
20
|
+
TEMP_PREFIX: str = STATIC_CONFIG['api']['jobs']['temp_prefix']
|
20
21
|
|
21
22
|
|
22
23
|
def get_actions(self):
|
@@ -37,43 +38,28 @@ async def do_action_async(
|
|
37
38
|
callback_function: Callable[[str], None] = partial(print, end=''),
|
38
39
|
) -> SuccessTuple:
|
39
40
|
"""
|
40
|
-
|
41
|
+
Execute an action as a temporary remote job.
|
41
42
|
"""
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
while True:
|
64
|
-
try:
|
65
|
-
line = await websocket.recv()
|
66
|
-
if asyncio.iscoroutinefunction(callback_function):
|
67
|
-
await callback_function(line)
|
68
|
-
else:
|
69
|
-
callback_function(line)
|
70
|
-
except KeyboardInterrupt:
|
71
|
-
await websocket.close()
|
72
|
-
break
|
73
|
-
except websockets_exceptions.ConnectionClosedOK:
|
74
|
-
break
|
75
|
-
|
76
|
-
return True, "Success"
|
43
|
+
from meerschaum._internal.arguments import remove_api_executor_keys
|
44
|
+
from meerschaum.utils.misc import generate_password
|
45
|
+
sysargs = remove_api_executor_keys(sysargs)
|
46
|
+
|
47
|
+
job_name = TEMP_PREFIX + generate_password(12)
|
48
|
+
job = mrsm.Job(job_name, sysargs, executor_keys=str(self))
|
49
|
+
|
50
|
+
start_success, start_msg = job.start()
|
51
|
+
if not start_success:
|
52
|
+
return start_success, start_msg
|
53
|
+
|
54
|
+
await job.monitor_logs_async(
|
55
|
+
callback_function=callback_function,
|
56
|
+
stop_on_exit=True,
|
57
|
+
strip_timestamps=True,
|
58
|
+
)
|
59
|
+
|
60
|
+
success, msg = job.result
|
61
|
+
job.delete()
|
62
|
+
return success, msg
|
77
63
|
|
78
64
|
|
79
65
|
def do_action_legacy(
|
@@ -277,6 +277,7 @@ async def monitor_logs_async(
|
|
277
277
|
"""
|
278
278
|
Monitor a job's log files and await a callback with the changes.
|
279
279
|
"""
|
280
|
+
import traceback
|
280
281
|
from meerschaum.jobs import StopMonitoringLogs
|
281
282
|
from meerschaum.utils.formatting._jobs import strip_timestamp_from_line
|
282
283
|
|
meerschaum/jobs/_Job.py
CHANGED
@@ -60,9 +60,10 @@ class Job:
|
|
60
60
|
sysargs: Union[List[str], str, None] = None,
|
61
61
|
env: Optional[Dict[str, str]] = None,
|
62
62
|
executor_keys: Optional[str] = None,
|
63
|
+
delete_after_completion: bool = False,
|
63
64
|
_properties: Optional[Dict[str, Any]] = None,
|
64
|
-
_rotating_log
|
65
|
-
_stdin_file
|
65
|
+
_rotating_log=None,
|
66
|
+
_stdin_file=None,
|
66
67
|
_status_hook: Optional[Callable[[], str]] = None,
|
67
68
|
_result_hook: Optional[Callable[[], SuccessTuple]] = None,
|
68
69
|
_externally_managed: bool = False,
|
@@ -85,6 +86,9 @@ class Job:
|
|
85
86
|
executor_keys: Optional[str], default None
|
86
87
|
If provided, execute the job remotely on an API instance, e.g. 'api:main'.
|
87
88
|
|
89
|
+
delete_after_completion: bool, default False
|
90
|
+
If `True`, delete this job when it has finished executing.
|
91
|
+
|
88
92
|
_properties: Optional[Dict[str, Any]], default None
|
89
93
|
If provided, use this to patch the daemon's properties.
|
90
94
|
"""
|
@@ -146,6 +150,9 @@ class Job:
|
|
146
150
|
if env:
|
147
151
|
self._properties_patch.update({'env': env})
|
148
152
|
|
153
|
+
if delete_after_completion:
|
154
|
+
self._properties_patch.update({'delete_after_completion': delete_after_completion})
|
155
|
+
|
149
156
|
daemon_sysargs = (
|
150
157
|
self._daemon.properties.get('target', {}).get('args', [None])[0]
|
151
158
|
if self._daemon is not None
|
@@ -245,7 +252,7 @@ class Job:
|
|
245
252
|
return True, f"{self} is already running."
|
246
253
|
|
247
254
|
success, msg = self.daemon.run(
|
248
|
-
keep_daemon_output=
|
255
|
+
keep_daemon_output=(not self.delete_after_completion),
|
249
256
|
allow_dirty_run=True,
|
250
257
|
)
|
251
258
|
if not success:
|
@@ -407,7 +414,6 @@ class Job:
|
|
407
414
|
)
|
408
415
|
return asyncio.run(monitor_logs_coroutine)
|
409
416
|
|
410
|
-
|
411
417
|
async def monitor_logs_async(
|
412
418
|
self,
|
413
419
|
callback_function: Callable[[str], None] = partial(print, end='', flush=True),
|
@@ -418,8 +424,8 @@ class Job:
|
|
418
424
|
strip_timestamps: bool = False,
|
419
425
|
accept_input: bool = True,
|
420
426
|
_logs_path: Optional[pathlib.Path] = None,
|
421
|
-
_log
|
422
|
-
_stdin_file
|
427
|
+
_log=None,
|
428
|
+
_stdin_file=None,
|
423
429
|
debug: bool = False,
|
424
430
|
):
|
425
431
|
"""
|
@@ -466,6 +472,7 @@ class Job:
|
|
466
472
|
input_callback_function=input_callback_function,
|
467
473
|
stop_callback_function=stop_callback_function,
|
468
474
|
stop_on_exit=stop_on_exit,
|
475
|
+
strip_timestamps=strip_timestamps,
|
469
476
|
accept_input=accept_input,
|
470
477
|
debug=debug,
|
471
478
|
)
|
@@ -494,13 +501,15 @@ class Job:
|
|
494
501
|
await asyncio.sleep(sleep_time)
|
495
502
|
sleep_time = round(sleep_time * 1.1, 2)
|
496
503
|
continue
|
497
|
-
|
504
|
+
|
498
505
|
if stop_callback_function is not None:
|
499
506
|
try:
|
500
507
|
if asyncio.iscoroutinefunction(stop_callback_function):
|
501
508
|
await stop_callback_function(self.result)
|
502
509
|
else:
|
503
510
|
stop_callback_function(self.result)
|
511
|
+
except asyncio.exceptions.CancelledError:
|
512
|
+
break
|
504
513
|
except Exception:
|
505
514
|
warn(traceback.format_exc())
|
506
515
|
|
@@ -596,6 +605,8 @@ class Job:
|
|
596
605
|
)
|
597
606
|
try:
|
598
607
|
_ = asyncio.gather(*tasks, return_exceptions=True)
|
608
|
+
except asyncio.exceptions.CancelledError:
|
609
|
+
raise
|
599
610
|
except Exception:
|
600
611
|
warn(f"Failed to run async checks:\n{traceback.format_exc()}")
|
601
612
|
|
@@ -865,7 +876,9 @@ class Job:
|
|
865
876
|
"""
|
866
877
|
Return the job's Daemon label (joined sysargs).
|
867
878
|
"""
|
868
|
-
|
879
|
+
from meerschaum._internal.arguments import compress_pipeline_sysargs
|
880
|
+
sysargs = compress_pipeline_sysargs(self.sysargs)
|
881
|
+
return shlex.join(sysargs).replace(' + ', '\n+ ')
|
869
882
|
|
870
883
|
@property
|
871
884
|
def _externally_managed_file(self) -> pathlib.Path:
|
@@ -911,6 +924,16 @@ class Job:
|
|
911
924
|
self._env = {**default_env, **_env}
|
912
925
|
return self._env
|
913
926
|
|
927
|
+
@property
|
928
|
+
def delete_after_completion(self) -> bool:
|
929
|
+
"""
|
930
|
+
Return whether this job is configured to delete itself after completion.
|
931
|
+
"""
|
932
|
+
if '_delete_after_completion' in self.__dict__:
|
933
|
+
return self.__dict__.get('_delete_after_completion', False)
|
934
|
+
|
935
|
+
self._delete_after_completion = self.daemon.properties.get('delete_after_completion', False)
|
936
|
+
return self._delete_after_completion
|
914
937
|
|
915
938
|
def __str__(self) -> str:
|
916
939
|
sysargs = self.sysargs
|
meerschaum/jobs/systemd.py
CHANGED
@@ -42,7 +42,12 @@ class SystemdExecutor(Executor):
|
|
42
42
|
return [
|
43
43
|
service_name[len('mrsm-'):(-1 * len('.service'))]
|
44
44
|
for service_name in os.listdir(SYSTEMD_USER_RESOURCES_PATH)
|
45
|
-
if
|
45
|
+
if (
|
46
|
+
service_name.startswith('mrsm-')
|
47
|
+
and service_name.endswith('.service')
|
48
|
+
### Check for broken symlinks.
|
49
|
+
and (SYSTEMD_USER_RESOURCES_PATH / service_name).exists()
|
50
|
+
)
|
46
51
|
]
|
47
52
|
|
48
53
|
def get_job_exists(self, name: str, debug: bool = False) -> bool:
|
@@ -146,13 +151,18 @@ class SystemdExecutor(Executor):
|
|
146
151
|
STATIC_CONFIG['environment']['systemd_log_path']: service_logs_path.as_posix(),
|
147
152
|
STATIC_CONFIG['environment']['systemd_result_path']: result_path.as_posix(),
|
148
153
|
STATIC_CONFIG['environment']['systemd_stdin_path']: socket_path.as_posix(),
|
154
|
+
STATIC_CONFIG['environment']['systemd_delete_job']: (
|
155
|
+
'1'
|
156
|
+
if job.delete_after_completion
|
157
|
+
else '0',
|
158
|
+
),
|
149
159
|
})
|
150
160
|
|
151
161
|
### Allow for user-defined environment variables.
|
152
162
|
mrsm_env_vars.update(job.env)
|
153
163
|
|
154
164
|
environment_lines = [
|
155
|
-
f"Environment={key}={val}"
|
165
|
+
f"Environment={key}={shlex.quote(str(val))}"
|
156
166
|
for key, val in mrsm_env_vars.items()
|
157
167
|
]
|
158
168
|
environment_str = '\n'.join(environment_lines)
|
@@ -603,7 +613,8 @@ class SystemdExecutor(Executor):
|
|
603
613
|
|
604
614
|
check_timeout_interval = get_config('jobs', 'check_timeout_interval_seconds')
|
605
615
|
loop_start = time.perf_counter()
|
606
|
-
|
616
|
+
timeout_seconds = get_config('jobs', 'timeout_seconds')
|
617
|
+
while (time.perf_counter() - loop_start) < timeout_seconds:
|
607
618
|
if self.get_job_status(name, debug=debug) == 'stopped':
|
608
619
|
return True, 'Success'
|
609
620
|
|
@@ -630,12 +641,14 @@ class SystemdExecutor(Executor):
|
|
630
641
|
Delete a job's service.
|
631
642
|
"""
|
632
643
|
from meerschaum.config.paths import SYSTEMD_LOGS_RESOURCES_PATH
|
644
|
+
job = self.get_hidden_job(name, debug=debug)
|
633
645
|
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
646
|
+
if not job.delete_after_completion:
|
647
|
+
_ = self.stop_job(name, debug=debug)
|
648
|
+
_ = self.run_command(
|
649
|
+
['disable', self.get_service_name(name, debug=debug)],
|
650
|
+
debug=debug,
|
651
|
+
)
|
639
652
|
|
640
653
|
service_job_path = self.get_service_job_path(name, debug=debug)
|
641
654
|
try:
|
@@ -666,7 +679,6 @@ class SystemdExecutor(Executor):
|
|
666
679
|
warn(e)
|
667
680
|
return False, str(e)
|
668
681
|
|
669
|
-
job = self.get_hidden_job(name, debug=debug)
|
670
682
|
_ = job.delete()
|
671
683
|
|
672
684
|
return self.run_command(['daemon-reload'], debug=debug)
|
meerschaum/utils/prompt.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
meerschaum/__init__.py,sha256=6bn5zz7VInDP4fE_FGBMzJYrM6rQhBMJNQqsf1pU7eI,1701
|
2
2
|
meerschaum/__main__.py,sha256=r5UjYxH1WA6dGG9YGBPul5xOdgF3Iwl0X4dWDtXU-30,2646
|
3
3
|
meerschaum/_internal/__init__.py,sha256=ilC7utfKtin7GAvuN34fKyUQYfPyqH0Mm3MJF5iyEf4,169
|
4
|
-
meerschaum/_internal/entry.py,sha256=
|
5
|
-
meerschaum/_internal/arguments/__init__.py,sha256=
|
6
|
-
meerschaum/_internal/arguments/_parse_arguments.py,sha256=
|
4
|
+
meerschaum/_internal/entry.py,sha256=XtGl-PPg9dLFlfbh_Wd5DUAdpjxqwQCxQxDrVf18QEE,12026
|
5
|
+
meerschaum/_internal/arguments/__init__.py,sha256=_nSKKVLXNsJeSv-buxEZsx8_c0BAbkhRyE4nT6Bv6q0,541
|
6
|
+
meerschaum/_internal/arguments/_parse_arguments.py,sha256=dtGjoyuKpQJtABdkQPzK1OM3Hd9JqlL-aQl_046tNa0,15976
|
7
7
|
meerschaum/_internal/arguments/_parser.py,sha256=LfiVDTr1akj5D31qpJAAXKxMcnLQiD3jRpZtvvHWrAo,14917
|
8
8
|
meerschaum/_internal/docs/__init__.py,sha256=ZQYHWo6n0kfLLkyG36YXqTYvv2Pc7it5HZHMylT6cBA,126
|
9
|
-
meerschaum/_internal/docs/index.py,sha256=
|
9
|
+
meerschaum/_internal/docs/index.py,sha256=LOxk1p_ChRkijfRGOB6UnoUEzt-SvtnMO56fTcSIMsU,24390
|
10
10
|
meerschaum/_internal/gui/__init__.py,sha256=KF6Opae0aBOjIndMZ2txoPs7ozCXRlR-lcTsicLO7fc,1313
|
11
11
|
meerschaum/_internal/gui/app/__init__.py,sha256=rKUa8hHk6Fai-PDF61tQcpT1myxKcfmvEMDHxThNp7o,1565
|
12
12
|
meerschaum/_internal/gui/app/_windows.py,sha256=-VHdjTzA3V596fVqnbmTxemONSp_80-sTNJ0CTB8FwU,2632
|
@@ -22,7 +22,7 @@ meerschaum/_internal/term/TermPageHandler.py,sha256=Rt5S47Pr_3HLJc8xIXpZUczYE_Dw
|
|
22
22
|
meerschaum/_internal/term/__init__.py,sha256=eXjfRzpnASWomB4xpY2AfzC_CLenkInNnVWSLZEexlg,1638
|
23
23
|
meerschaum/_internal/term/tools.py,sha256=dXVAimKD-Yv2fg2WOTr0YGBY7XDKjQqw-RizcS65YVI,727
|
24
24
|
meerschaum/actions/__init__.py,sha256=MHPs8aRBhbZQXnqd_6tVtisTrNCgPAPgnNcXYbn0zP8,11640
|
25
|
-
meerschaum/actions/api.py,sha256=
|
25
|
+
meerschaum/actions/api.py,sha256=nRHGeMTy0njI9Qec_606GwSS0AnoxiGJodOS8uWpjfU,12539
|
26
26
|
meerschaum/actions/attach.py,sha256=UV19d9W_2WYcrf7BRz7k3mriDoX1V4rA4AKvbLdor0o,3106
|
27
27
|
meerschaum/actions/bootstrap.py,sha256=9D3cBHzgZbZyWy-Y7iQgk9bpTbKEhumFKbIIThZgPXI,14058
|
28
28
|
meerschaum/actions/clear.py,sha256=OoFZE0bK5m8s3GLNZcixuVT0DMj1izXVxGCATcmUGbI,4851
|
@@ -35,7 +35,7 @@ meerschaum/actions/install.py,sha256=jdhOrR_KlvinTKr0YJNkUHsnh5EY6OzA7cRq0Vnp1oU
|
|
35
35
|
meerschaum/actions/login.py,sha256=fNgsgkrFCn9wBQJY50SQhz2PwsN_TvEYYHnXK3JG4ig,4206
|
36
36
|
meerschaum/actions/os.py,sha256=dtoppoBhLzW3rLNF0SFovEfNxA4WJWt_9WrOGlS5KbA,2251
|
37
37
|
meerschaum/actions/pause.py,sha256=WI4dQFfjQ6ObWKUzwQ5FrtYPDyEcnxWwval8cSIzzJw,4221
|
38
|
-
meerschaum/actions/python.py,sha256=
|
38
|
+
meerschaum/actions/python.py,sha256=k3Oo64RhyPd49W8bSjYvm6DOXuqY17NpR6BmfZb3-os,4737
|
39
39
|
meerschaum/actions/register.py,sha256=bOQY0AlZlchsxPF27LL7G1OgdiCyj7mkBaD-PO6bFZc,14491
|
40
40
|
meerschaum/actions/reload.py,sha256=gMXeFBGVfyQ7uhKhYf6bLaDMD0fLPcA9BrLBSiuvWIc,508
|
41
41
|
meerschaum/actions/restart.py,sha256=6ffp3-X9eTEgunVSdD41HnOwqp71yjuSAmXJ5j39ONI,3038
|
@@ -44,7 +44,7 @@ meerschaum/actions/sh.py,sha256=fLfTJaacKu4sjLTRqEzzYlT2WbbdZBEczsKb6F-qAek,2026
|
|
44
44
|
meerschaum/actions/show.py,sha256=Ij6v5so9GHUrYVi7AhPfhHGjABBofXTPAljLFa2xuWA,28141
|
45
45
|
meerschaum/actions/sql.py,sha256=wYofwk1vGO96U2ncigGEfMtYMZeprz2FR1PRRZhkAPI,4311
|
46
46
|
meerschaum/actions/stack.py,sha256=7ODAxzmCx8i9AHxvkbr5ZtzUNPpY-iqlSVo4rZHMOw4,5900
|
47
|
-
meerschaum/actions/start.py,sha256=
|
47
|
+
meerschaum/actions/start.py,sha256=eYk4rUo8LijxK-GLbuH4j0dapv4BqzE5xw7-KaVcV2M,19419
|
48
48
|
meerschaum/actions/stop.py,sha256=5fdUw70YN-yuUWrC-NhA88cxr9FZ5NbssbQ8xXO8nFU,4632
|
49
49
|
meerschaum/actions/sync.py,sha256=AkH-1O5bkUC-UElQGr0lRhrX-z18ZY2nBPSy9EsW1Kc,17506
|
50
50
|
meerschaum/actions/tag.py,sha256=SJf5qFW0ccLXjqlTdkK_0MCcrCMdg6xhYrhKdco0hdA,3053
|
@@ -53,7 +53,7 @@ meerschaum/actions/upgrade.py,sha256=uhFhAPmguGDgxLa1UkVZnPQ-JRcbmOdExE38v-HrLvo
|
|
53
53
|
meerschaum/actions/verify.py,sha256=tY5slGpHiWiE0v9TDnjbmxSKn86zBnu9WBpixUgKNQU,4885
|
54
54
|
meerschaum/api/__init__.py,sha256=tjxyG8SQR_ymYuj3YUaadlguXBj-edL4kY_CxWYrYHk,7449
|
55
55
|
meerschaum/api/_chain.py,sha256=h8-WXUGXX6AqzdALfsBC5uv0FkAcLdHJXCGzqzuq89k,875
|
56
|
-
meerschaum/api/_events.py,sha256=
|
56
|
+
meerschaum/api/_events.py,sha256=6Bgch1x-R82tmnixLMYunqv8NumlkOn9ergBRU5-vdI,2247
|
57
57
|
meerschaum/api/_oauth2.py,sha256=SDdFbssy-9QoFBkuZZmep7mCX7jsn_zjqCbMXiksEVg,1647
|
58
58
|
meerschaum/api/_websockets.py,sha256=Rso85r4Yj8yUB-Oh8T8bGBvO5vCzZboryOjY-VnxdZQ,1601
|
59
59
|
meerschaum/api/dash/__init__.py,sha256=NkqnUP77c02J6sjWp5PGw48Hz9Dhov7gBO5f2mK2b40,2198
|
@@ -115,10 +115,10 @@ meerschaum/api/resources/templates/old_index.html,sha256=BDeOlcXhSsBH3-NaRtuX4Z1
|
|
115
115
|
meerschaum/api/resources/templates/secret.html,sha256=0QWkm4ZoN81Aw1pd2-62rGCvx3nXPHfFUoegj3Iy8Ls,141
|
116
116
|
meerschaum/api/resources/templates/termpage.html,sha256=qspXRuOkzqOn2mXw9mmUldzsvOHq_LyaywQ29CUevp0,4527
|
117
117
|
meerschaum/api/routes/__init__.py,sha256=jbkeFNl51Tg8aT5gWe560ZLZLojFJsLMe5IENRjRkb0,606
|
118
|
-
meerschaum/api/routes/_actions.py,sha256=
|
118
|
+
meerschaum/api/routes/_actions.py,sha256=9vRu4dsBfIBrfhkVWurvk8n-ClerlHxggiPgQhwP1Oo,3701
|
119
119
|
meerschaum/api/routes/_connectors.py,sha256=NNbcn5xWhKqw2PqueSEaqRaZ95hFGDKazG5lE7gsssc,1849
|
120
120
|
meerschaum/api/routes/_index.py,sha256=QI6CBo6pI2Zi0a6fJHDjZfiLa9f4okb0BGe3A_JD0kM,578
|
121
|
-
meerschaum/api/routes/_jobs.py,sha256=
|
121
|
+
meerschaum/api/routes/_jobs.py,sha256=znS8Pt5kvrRpv_ZOwRftbvQ1jpNfQ1-ngMy7kPgI5iM,11567
|
122
122
|
meerschaum/api/routes/_login.py,sha256=psPKmFkXgYVX83NepqwIhaLsQ5uWgOc4F2QZtPGxY1A,2482
|
123
123
|
meerschaum/api/routes/_misc.py,sha256=05--9ZVFeaCgZrHER2kA3SYdK4TyfkEXOCjLvPbum-w,2469
|
124
124
|
meerschaum/api/routes/_pipes.py,sha256=1gBuE4E-QvIK_kmbmiw7uLcXjnIobFI1t4tb2skpp6E,21592
|
@@ -140,7 +140,7 @@ meerschaum/config/_preprocess.py,sha256=-AEA8m_--KivZwTQ1sWN6LTn5sio_fUr2XZ51BO6
|
|
140
140
|
meerschaum/config/_read_config.py,sha256=WFZKIXZMDe_ca0ES7ivgM_mnwShvFxLdoeisT_X5-h0,14720
|
141
141
|
meerschaum/config/_shell.py,sha256=46_m49Txc5q1rGfCgO49ca48BODx45DQJi8D0zz1R18,4245
|
142
142
|
meerschaum/config/_sync.py,sha256=oK2ZujO2T1he08BXCFyiniBUevNGWSQKXLcS_jRv_7Y,4155
|
143
|
-
meerschaum/config/_version.py,sha256=
|
143
|
+
meerschaum/config/_version.py,sha256=oyqZNQpNjf51DwGo7AplCiountsHT5TMqXee0KIj5tk,71
|
144
144
|
meerschaum/config/paths.py,sha256=JjibeGN3YAdSNceRwsd42aNmeUrIgM6ndzC8qZAmNI0,621
|
145
145
|
meerschaum/config/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
146
146
|
meerschaum/config/stack/__init__.py,sha256=Yt7GNzC_hz7iUDZ4gVho_lugJO2DnXgnMtsMG_ReoRg,9114
|
@@ -148,16 +148,16 @@ meerschaum/config/stack/grafana/__init__.py,sha256=LNXQw2FvHKrD68RDhqDmi2wJjAHaK
|
|
148
148
|
meerschaum/config/stack/mosquitto/__init__.py,sha256=-OwOjq8KiBoSH_pmgCAAF3Dp3CRD4KgAEdimZSadROs,186
|
149
149
|
meerschaum/config/stack/mosquitto/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
150
150
|
meerschaum/config/stack/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
151
|
-
meerschaum/config/static/__init__.py,sha256=
|
151
|
+
meerschaum/config/static/__init__.py,sha256=SLAcxX69MFiZBbonn3YiBxOHtdkPoz5Sbtz0Tt9CjKY,5225
|
152
152
|
meerschaum/connectors/Connector.py,sha256=utNV3Fy5DhUVbQE-vtm7enH5rH2gxQERmgmP7PUzH30,6682
|
153
153
|
meerschaum/connectors/__init__.py,sha256=fLoatCZV2hsDffYEg2aBzqPLzM_2o1_Z3X2iZvLwoqA,12503
|
154
154
|
meerschaum/connectors/parse.py,sha256=sDeI2OIS9Inwhbn9jkFAXxOPnmmAHqsuHiiHfWjVnSA,4307
|
155
155
|
meerschaum/connectors/poll.py,sha256=gIY9TvFBqMvMNQvR0O2No7koLLz2PjfExBr_Dsosgpg,7363
|
156
156
|
meerschaum/connectors/api/APIConnector.py,sha256=MPYeImCBAeSpueYis5CxkfT7kkV82MnGKDLoNE8uOVw,5017
|
157
157
|
meerschaum/connectors/api/__init__.py,sha256=JwKrGtuE5aOd2VnsRwudFBYyBf5IxczOwPVdNvCUgSQ,205
|
158
|
-
meerschaum/connectors/api/_actions.py,sha256=
|
158
|
+
meerschaum/connectors/api/_actions.py,sha256=gd3F8i5BvN8XRvMcPvPVR8sc1DeLzgoBHdDhc8PtACE,3966
|
159
159
|
meerschaum/connectors/api/_fetch.py,sha256=Khq9AFr1nk8Dsmcedb77aWhAuHw0JGgVeahDG95Q5MQ,2072
|
160
|
-
meerschaum/connectors/api/_jobs.py,sha256=
|
160
|
+
meerschaum/connectors/api/_jobs.py,sha256=N5lpHFGG10jlVgaJeWAOTuLBQw3AdgjXsEPpp1YwZQE,11270
|
161
161
|
meerschaum/connectors/api/_login.py,sha256=5GsD-B214vr5EYfM3XrTUs1sTFApxZA-9dNxq8oNSyg,2050
|
162
162
|
meerschaum/connectors/api/_misc.py,sha256=OZRZBYOokKIEjmQaR8jUYgu6ZRn9VzXBChzR8CfDv_w,1092
|
163
163
|
meerschaum/connectors/api/_pipes.py,sha256=Nnc-IShiTkCia548dePymKosQCcl2InFCyUX3Q-Xx6Q,20604
|
@@ -201,9 +201,9 @@ meerschaum/core/Plugin/__init__.py,sha256=UXg64EvJPgI1PCxkY_KM02-ZmBm4FZpLPIQR_u
|
|
201
201
|
meerschaum/core/User/_User.py,sha256=CApB7Y0QJL6S9QOCCfrG4SbPuPXJ9AsAYQ5pASMP_Aw,6527
|
202
202
|
meerschaum/core/User/__init__.py,sha256=lJ7beIZTG9sO4dAi3367fFBl17dXYEWHKi7HoaPlDyk,193
|
203
203
|
meerschaum/jobs/_Executor.py,sha256=qM62BhFTM4tyJ7p90KOM0y3qyeRY9k3ZV_aTDJMHnO8,1682
|
204
|
-
meerschaum/jobs/_Job.py,sha256=
|
204
|
+
meerschaum/jobs/_Job.py,sha256=XnvS-qN12907t23OG1suBJ5k_TDuXkS1diNE_1xNAF0,32152
|
205
205
|
meerschaum/jobs/__init__.py,sha256=ieruFbPxozkgyYMEO5wWQ4OXqfD5Pw9VtFWGub6DQxs,11970
|
206
|
-
meerschaum/jobs/systemd.py,sha256=
|
206
|
+
meerschaum/jobs/systemd.py,sha256=Rq-tsDPslG17ZhpKMrVJ5r8Z0IPr6DEc9APObfIoXCg,24614
|
207
207
|
meerschaum/plugins/_Plugin.py,sha256=p6j39tm-xrZENBq-eGtixBuXxLLddtEKWRCRFNqpRu0,34086
|
208
208
|
meerschaum/plugins/__init__.py,sha256=trMQ53qgP7ikJhhV_uXzqJw6X1NDz2rPOGXFk40bb1Y,26190
|
209
209
|
meerschaum/plugins/bootstrap.py,sha256=qg9MQ1YAU8ShwGqWDl38WjiXLIxDPl95pSIGDLN9rOw,11423
|
@@ -216,7 +216,7 @@ meerschaum/utils/misc.py,sha256=2f0wLQ0ymdOh5-5iP-jlyUxSUStvQAn5ot5_0GZTg-8,4638
|
|
216
216
|
meerschaum/utils/networking.py,sha256=Sr_eYUGW8_UV9-k9LqRFf7xLtbUcsDucODyLCRsFRUc,1006
|
217
217
|
meerschaum/utils/pool.py,sha256=vkE42af4fjrTEJTxf6Ek3xGucm1MtEkpsSEiaVzNKHs,2655
|
218
218
|
meerschaum/utils/process.py,sha256=o7UtTQX87YGkg2dItPhlvN7gNQPkElXTYSzKf5Ro8Uc,7474
|
219
|
-
meerschaum/utils/prompt.py,sha256=
|
219
|
+
meerschaum/utils/prompt.py,sha256=0asF_ndumQIN7p5kEOzK-ldsdE4m8FFapcT3-4wgPi8,19010
|
220
220
|
meerschaum/utils/schedule.py,sha256=WSJ0eDGvCVxkQwbUc_7vNm0uuKf9WJcY2NCZZdbnZrk,10837
|
221
221
|
meerschaum/utils/sql.py,sha256=4sCNEpgUd6uFz6ySs4nnUMVaOT0YAvPM1ZlQYJTSF-0,46656
|
222
222
|
meerschaum/utils/threading.py,sha256=3N8JXPAnwqJiSjuQcbbJg3Rv9-CCUMJpeQRfKFR7MaA,2489
|
@@ -241,11 +241,11 @@ meerschaum/utils/packages/_packages.py,sha256=GzbJ0kxW_EQogXmY4vguRkUyad42cshFs7
|
|
241
241
|
meerschaum/utils/packages/lazy_loader.py,sha256=VHnph3VozH29R4JnSSBfwtA5WKZYZQFT_GeQSShCnuc,2540
|
242
242
|
meerschaum/utils/venv/_Venv.py,sha256=sBnlmxHdAh2bx8btfVoD79-H9-cYsv5lP02IIXkyECs,3553
|
243
243
|
meerschaum/utils/venv/__init__.py,sha256=bLAWnllKDuE_z6bLk7gLh4mI3Sp1j5hsboTqPKOQq84,24361
|
244
|
-
meerschaum-2.3.
|
245
|
-
meerschaum-2.3.
|
246
|
-
meerschaum-2.3.
|
247
|
-
meerschaum-2.3.
|
248
|
-
meerschaum-2.3.
|
249
|
-
meerschaum-2.3.
|
250
|
-
meerschaum-2.3.
|
251
|
-
meerschaum-2.3.
|
244
|
+
meerschaum-2.3.5.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
|
245
|
+
meerschaum-2.3.5.dist-info/METADATA,sha256=c4wAuUfc77pQKQBHZ0hZiSgrztA-8V5Ph9rkgiOPAyQ,24006
|
246
|
+
meerschaum-2.3.5.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
|
247
|
+
meerschaum-2.3.5.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
248
|
+
meerschaum-2.3.5.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
|
249
|
+
meerschaum-2.3.5.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
|
250
|
+
meerschaum-2.3.5.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
251
|
+
meerschaum-2.3.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|