Skip to content

Commit

Permalink
Refactor the stop command (#71)
Browse files Browse the repository at this point in the history
Now several subfunctions are factored out from the rather lengthy `execute` function, in particular for stopping a native binary and for stopping a container.
  • Loading branch information
SimonL22 authored Dec 24, 2024
1 parent 4be0a47 commit ddf751e
Showing 1 changed file with 36 additions and 26 deletions.
62 changes: 36 additions & 26 deletions src/qlever/commands/stop.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
from __future__ import annotations

import re

import psutil

from qlever.command import QleverCommand
from qlever.commands.status import StatusCommand
from qlever.containerize import Containerize
from qlever.log import log
from qlever.util import show_process_info

# try to kill the given process, return true iff it was killed successfully.
# the process_info is used for logging.
def stop_process(proc, pinfo):
try:
proc.kill()
log.info(f"Killed process {pinfo['pid']}")
return True
except Exception as e:
log.error(f"Could not kill process with PID "
f"{pinfo['pid']} ({e}) ... try to kill it "
f"manually")
log.info("")
show_process_info(proc, "", show_heading=True)
return False


# try to stop and remove container. return True iff it was stopped
# successfully. Gives log info accordingly.
def stop_container(server_container):
for container_system in Containerize.supported_systems():
if Containerize.stop_and_remove_container(
container_system, server_container):
log.info(f"{container_system.capitalize()} container with "
f"name \"{server_container}\" stopped "
f" and removed")
return True
return False


class StopCommand(QleverCommand):
"""
Expand All @@ -20,7 +45,7 @@ def __init__(self):
pass

def description(self) -> str:
return ("Stop QLever server for a given datasedataset or port")
return "Stop QLever server for a given datasedataset or port"

def should_have_qleverfile(self) -> bool:
return True
Expand Down Expand Up @@ -54,46 +79,31 @@ def execute(self, args) -> bool:
# First check if there is container running and if yes, stop and remove
# it (unless the user has specified `--no-containers`).
if not args.no_containers:
for container_system in Containerize.supported_systems():
if Containerize.stop_and_remove_container(
container_system, args.server_container):
log.info(f"{container_system.capitalize()} container with "
f"name \"{args.server_container}\" stopped "
f" and removed")
return True
if stop_container(args.server_container):
return True

# Check if there is a process running on the server port using psutil.
#
# NOTE: On MacOS, some of the proc's returned by psutil.process_iter()
# no longer exist when we try to access them, so we just skip them.
for proc in psutil.process_iter():
try:
pinfo = proc.as_dict(
attrs=['pid', 'username', 'create_time',
'memory_info', 'cmdline'])
attrs=['pid', 'username', 'create_time',
'memory_info', 'cmdline'])
cmdline = " ".join(pinfo['cmdline'])
except Exception as e:
log.debug(f"Error getting process info: {e}")
return False
if re.search(cmdline_regex, cmdline):
log.info(f"Found process {pinfo['pid']} from user "
f"{pinfo['username']} with command line: {cmdline}")
log.info("")
try:
proc.kill()
log.info(f"Killed process {pinfo['pid']}")
except Exception as e:
log.error(f"Could not kill process with PID "
f"{pinfo['pid']} ({e}) ... try to kill it "
f"manually")
log.info("")
show_process_info(proc, "", show_heading=True)
return False
return True
return stop_process(proc, pinfo)

# If no matching process found, show a message and the output of the
# status command.
message = "No matching process found" if args.no_containers else \
"No matching process or container found"
"No matching process or container found"
log.error(message)
args.cmdline_regex = "^ServerMain.* -i [^ ]*"
log.info("")
Expand Down

0 comments on commit ddf751e

Please sign in to comment.