Running a Script in Docker

Here is a simple example of a wrapper script to call a docker image using the docker python library.

import argparse

import docker

parser = argparse.ArgumentParser(description="Docker test script")
parser.add_argument("--phrase", type=str)

if __name__ == "__main__":
    args = parser.parse_args()
    client = docker.from_env()
    print(client.containers.run("docker/whalesay", f"cowsay {args.phrase}").decode('utf-8'))

When running in this configuration, whatever environment the worker is running in must have access to docker. Additionally, if you want assets generated by a dockerfile, you should mount the current working directory to wherever the docker image outputs files to. For example, if a docker image output files to /output, the above invocation would be changed to:

import argparse
import os

import docker
from docker.types import Mount

parser = argparse.ArgumentParser(description="Docker test script")

if __name__ == "__main__":
    args = parser.parse_args()
    client = docker.from_env()
    # IMPORTANT #
    # This assumes we have a host directory for media storage (the MEDIA_ROOT setting) that has the same
    # path inside the container as the host (e.g. volume: /host/path:/host/path)
    wooey_data_dir = os.getcwd()
    print(
        client.containers.run(
            image="busybox",
            command=f"dd if=/dev/urandom of=/output/test.garbage bs=1M count=1",
            mounts=[Mount(target="/output", source=wooey_data_dir, type='bind')],
            ).decode(
            "utf-8"
        )
    )

If you are running Wooey inside Docker, then this becomes a bit more complicated as the data directory may be on a mount point different on the host than inside the container. The following script shows how to handle these scenarios:

import argparse
import os

import django
django.setup()
from django.conf import settings

import docker
from docker.types import Mount

parser = argparse.ArgumentParser(description="Docker test script")

if __name__ == "__main__":
    args = parser.parse_args()
    client = docker.from_env()
    # our volume is called `wooey_user_uploads`. If we had a NAS, then we could simplify this by ensuring the path
    # inside the container matches the path on the host -- in which case the above snippet is all that is needed.
    volume = client.volumes.get('wooey_user_uploads')
    volume_mount = volume.attrs['Mountpoint']
    current_dir = os.getcwd()
    wooey_data_dir = os.path.join(volume_mount.rstrip('/'), current_dir.replace(settings.MEDIA_ROOT, '').lstrip('/'))
    print(
        client.containers.run(
            image="busybox",
            command=f"dd if=/dev/urandom of=/output/test.garbage bs=1M count=1",
            mounts=[Mount(target="/output", source=wooey_data_dir, type='bind')],
            ).decode(
            "utf-8"
        )
    )