initial commit
This commit is contained in:
commit
e7552a277a
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Otherworld
|
||||
Otherworld creates a persistent Docker container running your choice of GNU/Linux distribution, with a matching user account (no need to run everything as root) and with x11 and PulseAudio mappings. The container is created the first time you run otherworld and sticks around until you dispose of it manually.
|
||||
|
||||
## Usage
|
||||
### Run command as user
|
||||
`otherworld <command> <arguments>`
|
||||
|
||||
### Run command as root
|
||||
`otherworld --sudo <command> <arguments>`
|
||||
|
||||
### Run command without printing anything else
|
||||
`otherworld --quiet <command> <arguments>`
|
||||
|
||||
### Remove the container
|
||||
`otherworld --rm`
|
||||
|
||||
## Environment variables
|
||||
### OW_IMAGE
|
||||
Image to create the container with (default: `debian:stable`)
|
||||
|
||||
### OW_CONTAINER
|
||||
Name to assign to container (default: `overworld_$USER`)
|
||||
|
||||
## License
|
||||
[GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html) or later
|
124
otherworld
Executable file
124
otherworld
Executable file
@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
from subprocess import 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",
|
||||
"-v", f"{home}/otherworld:{inner_user}/otherworld",
|
||||
"-w", inner_user,
|
||||
"--shm-size", "2g",
|
||||
"--privileged"]
|
||||
|
||||
DOCKER_CREATE_OPTIONS = ["-t"] + create_x11_mapping()
|
||||
|
||||
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):
|
||||
call(["docker", "exec", container, "adduser", user, "--gecos", ",,,", "--disabled-password"])
|
||||
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])
|
||||
|
||||
user = os.environ["USER"]
|
||||
image_name = os.environ.get("OW_IMAGE", IMAGE_NAME)
|
||||
container_name = os.environ.get("OW_CONTAINER", f"{CONTAINER_NAME}_{user}")
|
||||
|
||||
command = sys.argv[1:]
|
||||
command_user = user
|
||||
|
||||
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:]
|
||||
else:
|
||||
break
|
||||
|
||||
if len(command) == 0:
|
||||
command = DEFAULT_COMMAND
|
||||
|
||||
container = None
|
||||
try:
|
||||
container = docker_inspect(container_name)[0]
|
||||
except Exception:
|
||||
if not quiet:
|
||||
print(f">> Creating container: {container_name} (using image: {image_name})")
|
||||
docker_create(
|
||||
image_name,
|
||||
container_name,
|
||||
DOCKER_CREATE_OPTIONS,
|
||||
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)
|
||||
|
||||
if not quiet:
|
||||
print(f">> Welcome to {container_name} (IP {container['NetworkSettings']['IPAddress']})")
|
||||
docker_exec(container_name, command, command_user)
|
Loading…
x
Reference in New Issue
Block a user