Skip to content

Commit

Permalink
Support QLEVER_IS_RUNNING_IN_CONTAINER (#84)
Browse files Browse the repository at this point in the history
With ad-freiburg/qlever#1439, the recommended way to run QLever inside a container is to use the https://github.com/ad-freiburg/qlever-control script. Inside of the container, the environment variable `QLEVER_IS_RUNNING_IN_CONTAINER` is set, with the following two effects:

1. After `qlever setup-config <config name>`, the Qleverfile will contain the setting `SYSTEM = native` and not `SYSTEM = docker` and a warning message will be displayed that that is the case
2. When running `qlever ui`, an error message will be displayed that this command is disabled (reason: `qlever ui` runs another container, and we don't want containers inside of containers).
  • Loading branch information
hannahbast authored Nov 10, 2024
1 parent 7d47a3a commit 6b16696
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 54 deletions.
78 changes: 47 additions & 31 deletions src/qlever/commands/setup_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import subprocess
from os import environ
from pathlib import Path

from qlever.command import QleverCommand
Expand All @@ -15,33 +16,48 @@ class SetupConfigCommand(QleverCommand):

def __init__(self):
self.qleverfiles_path = Path(__file__).parent.parent / "Qleverfiles"
self.qleverfile_names = \
[p.name.split(".")[1]
for p in self.qleverfiles_path.glob("Qleverfile.*")]
self.qleverfile_names = [
p.name.split(".")[1] for p in self.qleverfiles_path.glob("Qleverfile.*")
]

def description(self) -> str:
return "Get a pre-configured Qleverfile"

def should_have_qleverfile(self) -> bool:
return False

def relevant_qleverfile_arguments(self) -> dict[str: list[str]]:
def relevant_qleverfile_arguments(self) -> dict[str : list[str]]:
return {}

def additional_arguments(self, subparser) -> None:
subparser.add_argument(
"config_name", type=str,
choices=self.qleverfile_names,
help="The name of the pre-configured Qleverfile to create")
"config_name",
type=str,
choices=self.qleverfile_names,
help="The name of the pre-configured Qleverfile to create",
)

def execute(self, args) -> bool:
# Show a warning if `QLEVER_OVERRIDE_SYSTEM_NATIVE` is set.
qlever_is_running_in_container = environ.get("QLEVER_IS_RUNNING_IN_CONTAINER")
if qlever_is_running_in_container:
log.warning(
"The environment variable `QLEVER_IS_RUNNING_IN_CONTAINER` is set, "
"therefore the Qleverfile is modified to use `SYSTEM = native` "
"(since inside the container, QLever should run natively)"
)
log.info("")
# Construct the command line and show it.
qleverfile_path = (self.qleverfiles_path
/ f"Qleverfile.{args.config_name}")
qleverfile_path = self.qleverfiles_path / f"Qleverfile.{args.config_name}"
setup_config_cmd = (
f"cat {qleverfile_path}"
f" | sed -E 's/(^ACCESS_TOKEN.*)/\\1_{get_random_string(12)}/'"
f"> Qleverfile")
f"cat {qleverfile_path}"
f" | sed -E 's/(^ACCESS_TOKEN.*)/\\1_{get_random_string(12)}/'"
)
if qlever_is_running_in_container:
setup_config_cmd += (
" | sed -E 's/(^SYSTEM[[:space:]]*=[[:space:]]*).*/\\1native/'"
)
setup_config_cmd += "> Qleverfile"
self.show(setup_config_cmd, only_show=args.show)
if args.show:
return False
Expand All @@ -51,31 +67,31 @@ def execute(self, args) -> bool:
if qleverfile_path.exists():
log.error("`Qleverfile` already exists in current directory")
log.info("")
log.info("If you want to create a new Qleverfile using "
"`qlever setup-config`, delete the existing Qleverfile "
"first")
log.info(
"If you want to create a new Qleverfile using "
"`qlever setup-config`, delete the existing Qleverfile "
"first"
)
return False

# Copy the Qleverfile to the current directory.
try:
subprocess.run(setup_config_cmd, shell=True, check=True,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
subprocess.run(
setup_config_cmd,
shell=True,
check=True,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
)
except Exception as e:
log.error(f"Could not copy \"{qleverfile_path}\""
f" to current directory: {e}")
log.error(
f'Could not copy "{qleverfile_path}"' f" to current directory: {e}"
)
return False

# If we get here, everything went well.
log.info(f"Created Qleverfile for config \"{args.config_name}\""
f" in current directory")
log.info(
f'Created Qleverfile for config "{args.config_name}"'
f" in current directory"
)
return True

# if config_name == "default":
# log.info("Since this is the default Qleverfile, you need to "
# "edit it before you can continue")
# log.info("")
# log.info("Afterwards, run `qlever` without arguments to see "
# "which actions are available")
# else:
# show_available_action_names()
# log.info("")
73 changes: 50 additions & 23 deletions src/qlever/commands/ui.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import subprocess
from os import environ

from qlever.command import QleverCommand
from qlever.containerize import Containerize
Expand All @@ -17,46 +18,70 @@ def __init__(self):
pass

def description(self) -> str:
return ("Launch the QLever UI web application")
return "Launch the QLever UI web application"

def should_have_qleverfile(self) -> bool:
return True

def relevant_qleverfile_arguments(self) -> dict[str: list[str]]:
return {"data": ["name"],
"server": ["host_name", "port"],
"ui": ["ui_port", "ui_config",
"ui_system", "ui_image", "ui_container"]}
def relevant_qleverfile_arguments(self) -> dict[str : list[str]]:
return {
"data": ["name"],
"server": ["host_name", "port"],
"ui": ["ui_port", "ui_config", "ui_system", "ui_image", "ui_container"],
}

def additional_arguments(self, subparser) -> None:
pass

def execute(self, args) -> bool:
# If QLEVER_OVERRIDE_DISABLE_UI is set, this command is disabled.
qlever_is_running_in_container = environ.get("QLEVER_IS_RUNNING_IN_CONTAINER")
if qlever_is_running_in_container:
log.error(
"The environment variable `QLEVER_OVERRIDE_DISABLE_UI` is set, "
"therefore `qlever ui` is not available (it should not be called "
"from inside a container)"
)
log.info("")
if not args.show:
log.info(
"For your information, showing the commands that are "
"executed when `qlever ui` is available:"
)
log.info("")

# Construct commands and show them.
server_url = f"http://{args.host_name}:{args.port}"
ui_url = f"http://{args.host_name}:{args.ui_port}"
pull_cmd = f"{args.ui_system} pull -q {args.ui_image}"
run_cmd = f"{args.ui_system} run -d " \
f"--publish {args.ui_port}:7000 " \
f"--name {args.ui_container} " \
f"{args.ui_image}"
exec_cmd = f"{args.ui_system} exec -it " \
f"{args.ui_container} " \
f"bash -c \"python manage.py configure " \
f"{args.ui_config} {server_url}\""
self.show("\n".join(["Stop running containers",
pull_cmd, run_cmd, exec_cmd]), only_show=args.show)
if args.show:
run_cmd = (
f"{args.ui_system} run -d "
f"--publish {args.ui_port}:7000 "
f"--name {args.ui_container} "
f"{args.ui_image}"
)
exec_cmd = (
f"{args.ui_system} exec -it "
f"{args.ui_container} "
f'bash -c "python manage.py configure '
f'{args.ui_config} {server_url}"'
)
self.show(
"\n".join(["Stop running containers", pull_cmd, run_cmd, exec_cmd]),
only_show=args.show,
)
if qlever_is_running_in_container or args.show:
return False

# Stop running containers.
for container_system in Containerize.supported_systems():
Containerize.stop_and_remove_container(
container_system, args.ui_container)
Containerize.stop_and_remove_container(container_system, args.ui_container)

# Check if the UI port is already being used.
if is_port_used(args.ui_port):
log.warning(f"It looks like the specified port for the UI ({args.ui_port}) is already in use. You can set another port in the Qleverfile in the [ui] section with the UI_PORT variable.")
log.warning(
f"It looks like the specified port for the UI ({args.ui_port}) is already in use. You can set another port in the Qleverfile in the [ui] section with the UI_PORT variable."
)

# Try to start the QLever UI.
try:
Expand All @@ -68,7 +93,9 @@ def execute(self, args) -> bool:
return False

# Success.
log.info(f"The QLever UI should now be up at {ui_url} ..."
f"You can log in as QLever UI admin with username and "
f"password \"demo\"")
log.info(
f"The QLever UI should now be up at {ui_url} ..."
f"You can log in as QLever UI admin with username and "
f'password "demo"'
)
return True

0 comments on commit 6b16696

Please sign in to comment.