otherworld/otherworld

182 lines
5.6 KiB
Python

#!/usr/bin/env python3
from subprocess import call, check_call, check_output
from time import sleep
import os
import sys
import json
CONTAINER_NAME = "otherworld"
IMAGE_NAME = "debian:stable"
def create_x11_mapping ():
home = os.environ["HOME"]
xdg_dir = os.environ["XDG_RUNTIME_DIR"]
display = os.environ["DISPLAY"]
inner_user = home
Xauthority = os.environ["XAUTHORITY"]
return ["-e", f"DISPLAY={display}",
"-v", "/tmp/.X11-unix/:/tmp/.X11-unix",
"-v", "/dev/snd:/dev/snd",
"-v", f"{xdg_dir}/pulse:/run/pulse:ro",
"-v", f"{home}/.gtkrc-2.0:{inner_user}/.gtkrc-2.0",
"-v", f"{home}/.config/gtk-3.0/:{inner_user}/.config/gtk-3.0",
"-v", f"{home}/.guix-profile/share/fonts/truetype:/user/share/fonts/truetype-guix",
"-v", f"{home}/.guix-profile/share/fonts/opentype:/usr/share/fonts/opentype-guix",
"-v", f"{Xauthority}:{inner_user}/.Xauthority",
"-w", inner_user,
"--shm-size", "2g",
"--privileged"]
DOCKER_CREATE_OPTIONS = ["-t"] + create_x11_mapping()
USER_VOLUMES = ["~/otherworld"]
BACKGROUND_COMMAND = None
DEFAULT_COMMAND = ["bash"]
def docker_inspect (container):
return json.loads(check_output(["docker", "inspect", container]))
def docker_create (image, container, options, command):
docker_command = ["docker", "create", "--name", container] + options + [image]
if command:
docker_command = docker_command + command
call(docker_command)
def docker_get_user (container, user):
return check_output(["docker", "exec", container, "groups", user])
def docker_create_user (container, user, uid=None):
command = ["docker", "exec", container, "adduser", user, "--gecos", ",,,", "--disabled-password"]
if uid is not None:
command = command + ["--uid", str(uid)]
call(command)
call(["docker", "exec", container, "chown", user, f"/home/{user}"])
def docker_start (container):
call(["docker", "start", container])
def docker_exec (container, command, user):
call(["docker", "exec", "--user", user, "-ti", container] + command)
def docker_rm (container):
call(["docker", "rm", "-f", container])
def docker_build (build_path, options, image_name):
check_call(["docker", "build"] + options + ["-t", image_name, build_path])
return image_name
def docker_pull (image_name):
check_call(["docker", "pull", image_name])
def resolve_build_path (build_path):
if build_path is not None and build_path.startswith("~/"):
build_path = f"{os.environ['HOME']}/{build_path[1:]}"
return build_path
def expand_user_volumes (volumes):
outer_home = os.environ["HOME"]
inner_home = outer_home
mappings = []
for volume in volumes:
source = volume
target = volume
if ":" in source:
(source, target) = source.split(":")
if source.startswith("~/"):
source = f"{outer_home}/{source[1:]}"
if source.startswith(outer_home) and not os.path.exists(source):
os.makedirs(source)
if target.startswith("~/"):
target = f"{inner_home}/{target[1:]}"
mappings = mappings + ["-v", f"{source}:{target}"]
return mappings
user = os.environ["USER"]
uid = int(check_output(["id", "-u"]).strip())
image_name = os.environ.get("OW_IMAGE", IMAGE_NAME)
container_name = os.environ.get("OW_CONTAINER", f"{CONTAINER_NAME}_{user}")
build_path = None
command = sys.argv[1:]
command_user = user
user_volumes = USER_VOLUMES
pull = False
quiet = False
while len(command) > 0:
arg = command[0]
if arg == "--quiet":
command = command[1:]
quiet = True
elif arg == "--rm":
if not quiet:
print(f">> Removing container: {container_name}")
docker_rm(container_name)
sys.exit(0)
elif arg == "--sudo":
command_user = "root"
command = command[1:]
elif arg.startswith("--container="):
container_name = arg[len("--container="):]
command = command[1:]
elif arg.startswith("--image="):
image_name = arg[len("--image="):]
command = command[1:]
elif arg.startswith("--build="):
build_path = arg[len("--build="):]
command = command[1:]
elif arg.startswith("--volume="):
user_volumes.append(arg[len("--volume="):])
command = command[1:]
elif arg == "--pull":
command = command[1:]
pull = True
else:
break
if len(command) == 0:
command = DEFAULT_COMMAND
container = None
try:
container = docker_inspect(container_name)[0]
except Exception:
build_path = resolve_build_path(build_path)
if build_path is not None:
image_name = docker_build(build_path, ["--pull"] if pull else [], f"image_{container_name}")
elif pull:
docker_pull(image_name)
if not quiet:
print(f">> Creating container: {container_name} (using image: {image_name})")
docker_create(
image_name,
container_name,
DOCKER_CREATE_OPTIONS + expand_user_volumes(user_volumes),
BACKGROUND_COMMAND
)
container = docker_inspect(container_name)[0]
if not container["State"]["Running"]:
if not quiet:
print(f">> Starting container: {container_name}")
docker_start(container_name)
container = docker_inspect(container_name)[0]
try:
docker_get_user(container_name, user)
except Exception:
if not quiet:
print(f">> Creating container user: {user}")
docker_create_user(container_name, user, uid)
if not quiet:
print(f">> Welcome to {container_name} (IP {container['NetworkSettings']['IPAddress']})")
docker_exec(container_name, command, command_user)