docker_tools_misc.py - Misc CLI tools for Docker¶
This files provides most of the subcommands for docker_tools.py - Tools for building and using the Runestone Servers in Docker.
If you want to add a new subcommand you must add it to the list in the add_commands function. That command ensures that docker_tools.py knows about the commands added in docker_tools_misc.py
Imports¶
These are listed in the order prescribed by PEP 8, with exceptions noted below.
There’s a fair amount of bootstrap code here to download and install required imports and their dependencies.
Standard library¶
Third-party¶
Local application¶
Globals¶
Subcommands for the CLI¶
shell
¶
Open a Bash shell in the Docker container, using the Python virtual environment for the Runestone servers.
Ask for an interactive console.
Skip a check, since the user will see any failures and because this raises an exception of the last command in the shell produced a non-zero exit code.
start_servers
¶
Run the web servers – nginx, web2py, and FastAPI – used by Runestone. Before starting the server, it will stop any currently-running servers.
Since click changes the way argument passing works, have a non-click version that’s easily callable from Python code.
def _start_servers(dev: bool) -> None:
ensure_in_docker()
bs_config = os.environ.get("BOOK_SERVER_CONFIG", "production")
w2p_config = os.environ.get("WEB2PY_CONFIG", "production")
if bs_config != w2p_config:
raise ValueError("web2py and bookserver configs do not match")
if bs_config == "development":
dev = True
sudo
doesn’t pass root’s env vars; provide only the env vars Celery needs when invoking it.
Celery runs as the www-data
user, so it doesn’t have access to the root-owned log files (which are symbolic links to files owned by root – changing permission doesn’t work). Therefore, redirect output (as root) to make this work.
This much match the address in runestone.template - nginx configuration.
If logging to a file, then Gunicorn tries to append to it (open the file with a mode of “a+”). This fails if the underlying “file” is actually stdout
or stderr
with the error io.UnsupportedOperation: File or stream is not seekable.
. So, redirect these instead.
Start the script to collect tickets and store them in the database. Most useful for a production environment with several worker containers.
stop_servers
¶
Shut down the web servers.
Shut down the web servers and celery, typically before running tests which involve the web servers.
restart_servers
¶
Restart the web servers and celery.
reloadbks
¶
Tell BookServer to reload the application.
send the HUP signal to the BookServer.
test
¶
Allow users to pass args directly to the underlying pytest
command – see the click docs.
Run unit tests. All tests are disabled by default; manually select which test to run.
PASSTHROUGH: These arguments are passed directly to the underlying “pytest” command. To pass options to this command, prefix this argument with “–”. For example, use “docker_tools.py test – -k test_just_this” instead of “docker_tools.py test -k test_just_this” (which produces an error).
ensure_in_docker()
if not bks and not rc and not rs:
sys.exit(
"ERROR: No tests selected to run. Pass any combination of --rc, --rs,\n"
"and/or --bks."
)
_stop_servers()
pytest = "$RUNESTONE_PATH/.venv/bin/pytest"
passthrough_args = " ".join(passthrough)
if bks:
xqt(f"{pytest} -v {passthrough_args}", cwd=env.BOOK_SERVER_PATH)
if rc:
xqt(f"{pytest} -v {passthrough_args}", cwd="/srv/RunestoneComponents")
if rs:
xqt(
f"{pytest} -v applications/runestone/tests {passthrough_args}",
cwd=env.WEB2PY_PATH,
)
wait
¶
This is primarily used by tests to wait until the servers are running.
Wait until the server is running, then report success or failure through the program’s exit code.
Wait for success or failure.
Misc¶
Add all subcommands in this file to the CLI.
Determine if we’re running in a Docker container.
This is difficult, and varies between OSes (Linux vs OS X) and Docker versions. Try a few different approaches and hope one works. This was taken from a site.
Newer Docker versions create a file – just look for that.
Try looking at the first process to see if it’s sh
.
We can’t find any evidence of Docker. Assume it’s not running.
If we’re not in Docker, then re-run this command inside Docker.
True to make this interactive (the -i
flag in docker exec
.)
Return value: True if already in Docker; the function calls sys.exit(0)
, ending the program, otherwise.
Get the name of the container running the Runestone servers.
Some subtleties:
Single-quote each argument before passing it.
Run it in the venv used when building Docker, since this avoids installing click globally.
Use env vars defined in the Dockerfile - create a container hosting the Runestone servers, rather than hard-coding paths. We want these env vars evaluated after the shell in Docker starts, not now, hence the use of
\$
and the surrounding double quotes.Use just the name, not the full path, of
sys.argv[0]
, since the filesystem is different in Docker. We assume that this command will be either in the path (with the venv activated).
Determine if the BookServer git repo is available, returning a Path to it if it exists, or None`
otherwise.
Volume detection strategy: don’t check just BookServer
– the volume may be mounted, but may not point to an actual filesystem path if the developer didn’t clone the BookServer repo. Instead, look for evidence that there are actually some files in this path.
Return the path to a file used to report the status of the container. Only for use inside Docker.