Skip to content

Commit 59ed3db

Browse files
committed
Merge pull request #912
2 parents 9d0023d + 6b80723 commit 59ed3db

19 files changed

Lines changed: 538 additions & 109 deletions

src/drunc/controller/interface/context.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515

1616
class ControllerContext(ShellContext): # boilerplatefest
17+
shell_id = "controller_shell"
18+
1719
def __init__(self):
1820
self.status_receiver = None
1921
self.took_control = False

src/drunc/process_manager/interface/cli_argument.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,10 @@ def validate_conf_string(ctx, param, boot_configuration):
77
return boot_configuration
88

99

10-
def add_query_options(at_least_one: bool, all_processes_by_default: bool = False):
11-
def wrapper(f0):
12-
f1 = click.option(
13-
"-s",
14-
"--session",
15-
type=str,
16-
default=None,
17-
help="Select the processes on a particular session",
18-
)(f0)
10+
def add_query_options_no_session(
11+
at_least_one: bool, all_processes_by_default: bool = False
12+
):
13+
def wrapper(f1):
1914
f2 = click.option(
2015
"-n",
2116
"--name",
@@ -41,3 +36,17 @@ def wrapper(f0):
4136
return generate_process_query(f4, at_least_one, all_processes_by_default)
4237

4338
return wrapper
39+
40+
41+
def add_query_options(at_least_one: bool, all_processes_by_default: bool = False):
42+
def wrapper(f0):
43+
f1 = click.option(
44+
"-s",
45+
"--session",
46+
type=str,
47+
default=None,
48+
help="Select the processes on a particular session",
49+
)(f0)
50+
return add_query_options_no_session(at_least_one, all_processes_by_default)(f1)
51+
52+
return wrapper

src/drunc/process_manager/interface/commands.py

Lines changed: 90 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import getpass
2+
from time import sleep
23

34
import click
45
from druncschema.process_manager_pb2 import LogRequest, ProcessQuery
@@ -11,7 +12,7 @@
1112
)
1213
from drunc.process_manager.interface.context import ProcessManagerContext
1314
from drunc.process_manager.utils import tabulate_process_instance_list
14-
from drunc.utils.shell_utils import InterruptedCommand
15+
from drunc.utils.shell_utils import InterruptedCommand, log_pm_cmd
1516
from drunc.utils.utils import get_logger
1617

1718

@@ -43,6 +44,7 @@ def boot(
4344
override_logs: bool,
4445
) -> None:
4546
log = get_logger("process_manager.shell")
47+
log_pm_cmd(obj)
4648
processes = obj.get_driver("process_manager").ps(ProcessQuery(user=user))
4749

4850
if len(processes.values) > 0:
@@ -129,6 +131,7 @@ def dummy_boot(
129131
session_name: str,
130132
) -> None:
131133
log = get_logger("process_manager.shell")
134+
log_pm_cmd(obj)
132135
log.debug(
133136
f"Running dummy_boot with {n_processes} processes for {sleep} seconds {n_sleeps} times, requested by user {user}"
134137
)
@@ -150,6 +153,16 @@ def dummy_boot(
150153
return
151154

152155

156+
@click.command("wait")
157+
@click.argument("sleep_time", type=int, default=1)
158+
@click.pass_obj
159+
def wait(obj: ProcessManagerContext, sleep_time: int) -> None:
160+
log = get_logger("process_manager.wait")
161+
log.info(f"Command [green]wait[/green] running for {sleep_time} seconds.")
162+
sleep(sleep_time) # seconds
163+
log.info(f"Command [green]wait[/green] ran for {sleep_time} seconds.")
164+
165+
153166
@click.command("terminate")
154167
@click.option(
155168
"-w",
@@ -161,6 +174,7 @@ def dummy_boot(
161174
@click.pass_obj
162175
def terminate(obj: ProcessManagerContext, width: int | None) -> None:
163176
log = get_logger("process_manager.shell")
177+
log_pm_cmd(obj)
164178
log.debug("Terminating")
165179
result = obj.get_driver("process_manager").terminate()
166180
if not result:
@@ -171,23 +185,35 @@ def terminate(obj: ProcessManagerContext, width: int | None) -> None:
171185
obj.delete_driver("controller")
172186

173187

188+
def kill_decorators(f):
189+
f = click.pass_obj(f)
190+
f = click.option(
191+
"-w",
192+
"--width",
193+
type=int,
194+
default=None,
195+
help="Table width. Default is automatically calculated",
196+
)(f)
197+
f = click.option(
198+
"--crash",
199+
is_flag=True,
200+
default=False,
201+
help="Simulate a crash: send SIGKILL without any cleanup, leaving the process manager in an unexpected-death state.",
202+
)(f)
203+
return f
204+
205+
174206
@click.command("kill")
175-
@click.option(
176-
"-w",
177-
"--width",
178-
type=int,
179-
default=None,
180-
help="Table width. Default is automatically calculated",
181-
)
182207
@add_query_options(at_least_one=True)
183-
@click.option(
184-
"--crash",
185-
is_flag=True,
186-
default=False,
187-
help="Simulate a crash: send SIGKILL without any cleanup, leaving the process manager in an unexpected-death state.",
188-
)
189-
@click.pass_obj
190-
def kill(obj: ProcessManagerContext, query: ProcessQuery, width: int | None) -> None:
208+
@kill_decorators
209+
def kill(obj, query, width):
210+
log_pm_cmd(obj)
211+
return kill_impl(obj, query, width)
212+
213+
214+
def kill_impl(
215+
obj: ProcessManagerContext, query: ProcessQuery, width: int | None
216+
) -> None:
191217
log = get_logger("process_manager.shell")
192218
log.debug(f"Killing with query {query}")
193219
result = obj.get_driver("process_manager").kill(query)
@@ -198,17 +224,27 @@ def kill(obj: ProcessManagerContext, query: ProcessQuery, width: int | None) ->
198224
) # rich tables require console printing
199225

200226

227+
def flush_decorators(f):
228+
f = click.pass_obj(f)
229+
f = click.option(
230+
"-w",
231+
"--width",
232+
type=int,
233+
default=None,
234+
help="Table width. Default is automatically calculated",
235+
)(f)
236+
return f
237+
238+
201239
@click.command("flush")
202-
@click.option(
203-
"-w",
204-
"--width",
205-
type=int,
206-
default=None,
207-
help="Table width. Default is automatically calculated",
208-
)
209240
@add_query_options(at_least_one=False, all_processes_by_default=True)
210-
@click.pass_obj
211-
def flush(
241+
@flush_decorators
242+
def flush(obj, query, width):
243+
log_pm_cmd(obj)
244+
return flush_impl(obj, query, width)
245+
246+
247+
def flush_impl(
212248
obj: ProcessManagerContext,
213249
query: ProcessQuery,
214250
width: int | None,
@@ -223,22 +259,34 @@ def flush(
223259
) # rich tables require console printing
224260

225261

262+
def logs_decorators(f):
263+
f = click.pass_obj(f)
264+
f = click.option("--grep", type=str, default=None)(f)
265+
f = click.option(
266+
"--how-far",
267+
type=int,
268+
show_default=True,
269+
default=100,
270+
help="How many lines one wants",
271+
)(f)
272+
return f
273+
274+
226275
@click.command("logs")
227276
@add_query_options(at_least_one=True)
228-
@click.option(
229-
"--how-far",
230-
type=int,
231-
show_default=True,
232-
default=100,
233-
help="How many lines one wants",
234-
)
235-
@click.option("--grep", type=str, default=None)
236-
@click.pass_obj
237-
def logs(
277+
@logs_decorators
278+
def logs(obj, how_far, grep, query):
279+
log_pm_cmd(obj)
280+
return logs_impl(obj, how_far, grep, query)
281+
282+
283+
def logs_impl(
238284
obj: ProcessManagerContext, how_far: int, grep: str, query: ProcessQuery
239285
) -> None:
240286
log = get_logger("process_manager.shell")
241-
log.debug(f"Running logs with query {query}")
287+
# TODO: MOVE BACK TO DEBUG BEFORE MERGE
288+
# THIS IS USEFUL FOR TESTING THOUGH
289+
log.error(f"Running logs with query {query}")
242290
log_req = LogRequest(
243291
how_far=how_far,
244292
query=query,
@@ -276,6 +324,11 @@ def logs(
276324
@add_query_options(at_least_one=True)
277325
@click.pass_obj
278326
def restart(obj: ProcessManagerContext, query: ProcessQuery) -> None:
327+
log_pm_cmd(obj)
328+
return restart_impl(obj, query)
329+
330+
331+
def restart_impl(obj: ProcessManagerContext, query: ProcessQuery) -> None:
279332
log = get_logger("process_manager.shell")
280333
log.debug(f"Restarting with query {query}")
281334
obj.get_driver("process_manager").restart(query)
@@ -306,6 +359,7 @@ def ps(
306359
width: int | None,
307360
) -> None:
308361
log = get_logger("process_manager.shell")
362+
log_pm_cmd(obj)
309363
log.debug(f"Running ps with query {query}")
310364
results = obj.get_driver("process_manager").ps(query)
311365
if not results:

src/drunc/process_manager/interface/context.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515

1616
class ProcessManagerContext(ShellContext): # boilerplatefest
17+
shell_id = "process_manager_shell"
18+
1719
def __init__(self, *args, **kwargs):
1820
self.status_receiver = None
1921
super(ProcessManagerContext, self).__init__(*args, **kwargs)

src/drunc/process_manager/interface/shell.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
ps,
1515
restart,
1616
terminate,
17+
wait,
1718
)
1819
from drunc.utils.grpc_utils import ServerUnreachable
1920
from drunc.utils.utils import (
@@ -59,6 +60,10 @@ def process_manager_shell(ctx, process_manager_address: str, log_level: str) ->
5960
# process_manager_shell_log.error(e.message) # TODO: Keep this for production branch, remove this from dev branch
6061
exit(1)
6162

63+
ctx.obj.get_driver("process_manager").send_msg(
64+
f"{getpass.getuser()} connected from {ctx.obj.shell_id}"
65+
)
66+
6267
# Manually add file handler to process manager log
6368
# Not possible to initialise logger immediately as it requires
6469
# knowledge of the log path
@@ -75,6 +80,9 @@ def process_manager_shell(ctx, process_manager_address: str, log_level: str) ->
7580
ctx.obj.start_listening(desc.broadcast)
7681

7782
def cleanup():
83+
ctx.obj.get_driver("process_manager").send_msg(
84+
f"{getpass.getuser()} disconnected from {ctx.obj.shell_id}"
85+
)
7886
ctx.obj.terminate()
7987
process_manager_log.warning(
8088
f"[green]{getpass.getuser()}[/green] disconnected from the process manager through a [green]drunc-process-manager-shell[/green]"
@@ -83,6 +91,7 @@ def cleanup():
8391
ctx.call_on_close(cleanup)
8492

8593
ctx.command.add_command(boot, "boot")
94+
ctx.command.add_command(wait, "wait")
8695
ctx.command.add_command(terminate, "terminate")
8796
ctx.command.add_command(kill, "kill")
8897
ctx.command.add_command(flush, "flush")

src/drunc/process_manager/k8s_process_manager.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
# Local Application Imports
1515
from druncschema.broadcast_pb2 import BroadcastType
16+
from druncschema.generic_pb2 import OutcomeFlag, OutcomeStatus
1617
from druncschema.process_manager_pb2 import (
1718
BootRequest,
1819
LogLines,
@@ -39,6 +40,7 @@
3940
from drunc.process_manager.configuration import (
4041
PROCESS_SHUTDOWN_ORDERING,
4142
ProcessManagerConfHandler,
43+
ProcessManagerTypes,
4244
)
4345
from drunc.process_manager.process_manager import ProcessManager
4446
from drunc.process_manager.utils import (
@@ -156,6 +158,8 @@ def run(self) -> None:
156158

157159

158160
class K8sProcessManager(ProcessManager):
161+
pm_type = ProcessManagerTypes.K8s
162+
159163
def __init__(self, configuration: ProcessManagerConfHandler, **kwargs) -> None:
160164
"""
161165
Manages processes as Kubernetes Pods.
@@ -1943,6 +1947,17 @@ def _logs_impl(self, log_request: LogRequest) -> LogLines:
19431947
lines=[f"Could not retrieve logs: {e.reason}"],
19441948
)
19451949

1950+
def _send_msg_impl(self, msg: str, peer: str) -> OutcomeStatus:
1951+
# Note: currently exact same implementation as ssh manager
1952+
# Although there is room here to change as necessary
1953+
try:
1954+
self.log.info(f"{msg}; from {peer}")
1955+
except Exception as e:
1956+
self.log.critical(f"Failed to receive message with exception {e}")
1957+
return OutcomeStatus(flag=OutcomeFlag.FAIL)
1958+
1959+
return OutcomeStatus(flag=OutcomeFlag.SUCCESS)
1960+
19461961
def _boot_impl(self, boot_request: BootRequest) -> ProcessInstanceList:
19471962
"""
19481963
Handles the 'boot' command from the gRPC interface.

src/drunc/process_manager/oks_parser.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from drunc.exceptions import DruncException, DruncSetupException
77
from drunc.process_manager.configuration import get_commandline_parameters
8-
from drunc.utils.utils import get_logger
8+
from drunc.utils.utils import file_is_read_only, get_logger
99

1010
if TYPE_CHECKING:
1111
import conffwk
@@ -75,8 +75,13 @@ def get_full_db_path(db_path: str) -> str:
7575
err_str = f"No files found in DUNEDAQ_DB_PATH matching {db_path}."
7676
raise DruncSetupException(err_str)
7777

78-
# If multiple matches are found, take the first instance that matches.
78+
# Prefer the first writable match; if every match is read-only, fall back to the first one.
7979
resolved_path = unique_matched_files[0]
80+
for matched_file in unique_matched_files:
81+
if not file_is_read_only(matched_file):
82+
resolved_path = matched_file
83+
break
84+
8085
log.debug(f"Path {db_path} resolved to {resolved_path}")
8186
return resolved_path
8287

0 commit comments

Comments
 (0)