0
\$\begingroup\$

Motivation

I like using so-called online judges and competitive programming sites to practice coding (e.g. CodeWars, Exercism, HackerRank, etc.). I was thinking that it would be nice to try write my own at some point. The first step, in my mind, would be to find a simple way to allow a user to input some code and have it run inside of a sandboxed environment. I decided to try to use Python to run Bash code inside of a Docker container and display the results.

Features

Here is what I want my program to do:

  • Launch a text editor for the user to type some Bash code

  • Execute the user's Bash code inside of a Docker container

  • Print the standard output obtained from the command execution

Implementation

Here is a small script I wrote which seems to have the desired result:

#!/usr/bin/env python3
# -*- encoding: utf8 -*-
"""docker_run_bash.py

Example of how to run a shell script inside of a docker container.
"""

from subprocess import call
import docker
import os
import sys
import tempfile

# Get the text editor from the shell, otherwise default to Vim
EDITOR = os.environ.get('EDITOR','vim')

# Select which docker container to use (default to busybox-bash)
container = sys.argv[1] if len(sys.argv)>=2 else "blang/busybox-bash"

# Instantiate a Docker client object
client = docker.from_env(assert_hostname=False)

# Create a temporary directory
with tempfile.TemporaryDirectory(dir="/tmp") as td:

    # Create a temporary file in that directory
    tf = tempfile.NamedTemporaryFile(dir=td, delete=False)

    # Write to the temporary file
    tf.write("# Enter command to execute...".encode())

    # Flush the I/O buffer to make sure the data is written to the file
    tf.flush()

    # Open the file with the text editor
    call([EDITOR, tf.name])

    # Get the path to the mounted file on the container
    mounted_file = os.path.join("/mnt/host", os.path.basename(tf.name))

    # Execute the script
    cmd_stdout = client.containers.run(
        container,
        "/bin/bash {}".format(mounted_file),
        remove=True,
        volumes={
            td: {
                'bind': '/mnt/host',
                'mode': 'ro',
            }
        }
    )

    # Delete the file
    os.unlink(tf.name)

    # Output the results
    print("Command output:\n{}".format(cmd_stdout.decode()))

Usage

Here is how I run the command:

python docker_run_bash.py

This launches Vim for me. Here is how I might edit the buffer:

  1 # Enter command to execute...
  2 hostname
  3
~

After saving the buffer and exiting Vim I get the following output:

Command output:
309038e0dd45
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.