Compare commits

...

8 Commits

View File

@@ -80,8 +80,9 @@ def docker_create_user (container, user, uid=None):
def docker_start (container): def docker_start (container):
call(["docker", "start", container]) call(["docker", "start", container])
def docker_exec (container, command, user, env={}, work_dir=None): def docker_exec (container, command, user, env={}, tty=True, work_dir=None):
call(["docker", "exec", "--user", user, "-ti"] + \ call(["docker", "exec", "--user", user] + \
(["-ti" if tty else "-i"]) + \
(["--workdir", work_dir] if work_dir else []) + \ (["--workdir", work_dir] if work_dir else []) + \
list(chain.from_iterable([["--env", f"{kv[0]}={kv[1]}"] for kv in env.items()])) + \ list(chain.from_iterable([["--env", f"{kv[0]}={kv[1]}"] for kv in env.items()])) + \
[container] + command) [container] + command)
@@ -92,6 +93,9 @@ def docker_commit (container, image_name):
def docker_rm (container): def docker_rm (container):
call(["docker", "rm", "-f", container]) call(["docker", "rm", "-f", container])
def docker_restart (container):
call(["docker", "restart", container])
def docker_rmi (container): def docker_rmi (container):
call(["docker", "rmi", "-f", container]) call(["docker", "rmi", "-f", container])
@@ -131,10 +135,72 @@ def expand_user_volumes (volumes):
return mappings return mappings
def find_config_files (config_name): def parse_command_line (command, config=None):
if config is None:
config = {
"actions": [],
"user_volumes": [],
"command_env": {}
}
while len(command) > 0:
arg = command[0]
if arg == "--quiet":
command = command[1:]
config['quiet'] = True
elif arg == "--notty":
command = command[1:]
config['tty'] = False
elif arg == "--rm":
config['actions'].append("rm")
command = command[1:]
elif arg == "--restart":
config['actions'].append("restart")
command = command[1:]
elif arg == "--rmi":
config['actions'].append("rmi")
command = command[1:]
elif arg == "--commit":
config['actions'].append("commit")
command = command[1:]
elif arg == "--sudo":
config['command_user'] = "root"
command = command[1:]
elif arg.startswith("--container="):
config['container_name'] = arg[len("--container="):]
command = command[1:]
elif arg.startswith("--image="):
config['image_name'] = arg[len("--image="):]
command = command[1:]
elif arg.startswith("--build="):
config['build_path'] = arg[len("--build="):]
command = command[1:]
elif arg.startswith("--volume="):
config['user_volumes'].append(arg[len("--volume="):])
command = command[1:]
elif arg.startswith("--env="):
env_pair = arg[len("--env="):]
(key, value) = env_pair.split("=", 1)
config['command_env'][key] = value
command = command[1:]
elif arg == "--cd":
config['command_workdir'] = os.getcwd()
command = command[1:]
elif arg.startswith("--cd="):
config['command_workdir'] = arg[len("--cd="):]
command = command[1:]
elif arg == "--pull":
command = command[1:]
config['pull'] = True
else:
break
return (config, command)
def find_config_files (config_names):
config_files = [] config_files = []
directory = os.path.abspath(os.getcwd()) directory = os.path.abspath(os.getcwd())
while True: while True:
for config_name in config_names:
config_file = os.path.join(directory, config_name) config_file = os.path.join(directory, config_name)
if os.path.exists(config_file): if os.path.exists(config_file):
config_files.append(config_file) config_files.append(config_file)
@@ -157,86 +223,69 @@ def load_config_files (namespace, config_files):
exec(config_code.read(), {}, namespace) exec(config_code.read(), {}, namespace)
return dict([item for item in namespace.items() if item[0] in config_keys]) return dict([item for item in namespace.items() if item[0] in config_keys])
def init_command_as_list (command):
if isinstance(command, str):
command = ["sh", "-c", command]
return command
# parse options and command from command line
(command_line_options, command) = parse_command_line(sys.argv[1:])
actions = command_line_options['actions']
quiet = command_line_options.get("quiet", False)
def log (message, *args, **kwargs):
if quiet:
return
print(">> {}".format(message.format(*args, **kwargs)))
tty = sys.stdout.isatty()
# default config
user = os.environ["USER"] user = os.environ["USER"]
uid = int(check_output(["id", "-u"]).strip()) config = {
image_name = os.environ.get("OW_IMAGE", None)
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
actions = []
command_env = {}
command_workdir = None
quiet = False
# load config from files
config = load_config_files({
"command": command, "command": command,
"user": user, "user": user,
"uid": uid, "uid": int(check_output(["id", "-u"]).strip()),
"command_user": command_user, "command_user": user,
"image_name": image_name, "image_name": os.environ.get("OW_IMAGE", None),
"container_name": container_name, "container_name": command_line_options.get("container_name", os.environ.get("OW_CONTAINER", f"{CONTAINER_NAME}_{user}")),
"build_path": build_path, "build_path": None,
"command_workdir": command_workdir, "command_workdir": None,
"user_volumes": user_volumes, "user_volumes": USER_VOLUMES,
"pull": pull, "pull": False,
"command_env": command_env, "command_env": {},
"cwd": os.getcwd() "cwd": os.getcwd(),
}, find_config_files(OTHERWORLD_CONFIG)) "tty": sys.stdout.isatty(),
locals().update(config) "log": log,
"root_init_command": [],
"root_init_commmands": [],
"user_init_command": [],
"user_init_commands": [],
"docker_create_options": []
}
while len(command) > 0: # load config from files
arg = command[0] config_files = find_config_files([
if arg == "--quiet": OTHERWORLD_CONFIG,
command = command[1:] "{}{}".format(config['container_name'], OTHERWORLD_CONFIG)
quiet = True ])
elif arg == "--rm":
actions.append("rm") for config_file in config_files:
command = command[1:] log("Loading config file {}", config_file)
elif arg == "--rmi":
actions.append("rmi") config = load_config_files(config, config_files)
command = command[1:]
elif arg == "--commit": # apply config from command line
actions.append("commit") for key, value in command_line_options.items():
command = command[1:] if key == "user_volumes":
elif arg == "--sudo": config['user_volumes'] = config['user_volumes'] + value
command_user = "root" elif key == "command_env":
command = command[1:] config['command_env'].update(value)
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.startswith("--env="):
env_pair = arg[len("--env="):]
(key, value) = env_pair.split("=", 1)
command_env[key] = value
command = command[1:]
elif arg == "--cd":
command_workdir = os.getcwd()
command = command[1:]
elif arg.startswith("--cd="):
command_workdir = arg[len("--cd="):]
command = command[1:]
elif arg == "--pull":
command = command[1:]
pull = True
else: else:
break config[key] = value
# update local variables (FIXME: rewrite to avoid doing this)
locals().update(config)
if command_workdir: if command_workdir:
user_volumes.append(command_workdir) user_volumes.append(command_workdir)
@@ -245,19 +294,20 @@ if command_workdir:
if actions: if actions:
if "commit" in actions: if "commit" in actions:
target_image_name = get_generated_image_name(container_name) target_image_name = get_generated_image_name(container_name)
if not quiet: log("Committing container: {} to image: {}", container_name, target_image_name)
print(f">> Committing container: {container_name} to image: {target_image_name}")
docker_commit(container_name, target_image_name) docker_commit(container_name, target_image_name)
if "restart" in actions:
log("Restarting container: {}", container_name)
docker_restart(container_name)
if "rm" in actions: if "rm" in actions:
if not quiet: log("Removing container: {}", container_name)
print(f">> Removing container: {container_name}")
docker_rm(container_name) docker_rm(container_name)
if "rmi" in actions: if "rmi" in actions:
target_image_name = get_generated_image_name(container_name) target_image_name = get_generated_image_name(container_name)
if not quiet: log("Removing image: {}", target_image_name)
print(f">> Removing image: {target_image_name}")
docker_rmi(target_image_name) docker_rmi(target_image_name)
if not command: if not command:
@@ -278,39 +328,45 @@ except Exception:
image_name = get_generated_image_name(container_name) image_name = get_generated_image_name(container_name)
try: try:
docker_inspect(image_name) docker_inspect(image_name)
if not quiet: log("Using default otherworld image {}", image_name)
print(f">> Using default otherworld image {image_name}")
except Exception: except Exception:
if not quiet: log("Default otherworld image {} not found, falling back to {}", image_name, DEFAULT_IMAGE_NAME)
print(f">> Default otherworld image {image_name} not found, falling back to {DEFAULT_IMAGE_NAME}")
image_name = DEFAULT_IMAGE_NAME image_name = DEFAULT_IMAGE_NAME
if pull: if pull:
docker_pull(image_name) docker_pull(image_name)
if not quiet: log("Creating container: {} (using image: {})", container_name, image_name)
print(f">> Creating container: {container_name} (using image: {image_name})")
docker_create( docker_create(
image_name, image_name,
container_name, container_name,
DOCKER_CREATE_OPTIONS + create_x11_mapping() + expand_user_volumes(user_volumes), DOCKER_CREATE_OPTIONS + config.get("docker_create_options", []) + create_x11_mapping() + expand_user_volumes(user_volumes),
BACKGROUND_COMMAND, { OTHERWORLD_LABEL: user } BACKGROUND_COMMAND, { OTHERWORLD_LABEL: user }
) )
container = docker_inspect(container_name)[0] container = docker_inspect(container_name)[0]
if not container["State"]["Running"]: if not container["State"]["Running"]:
if not quiet: log("Starting container: {}", container_name)
print(f">> Starting container: {container_name}")
docker_start(container_name) docker_start(container_name)
container = docker_inspect(container_name)[0] container = docker_inspect(container_name)[0]
try: try:
docker_get_user(container_name, user) docker_get_user(container_name, user)
except Exception: except Exception:
if not quiet: log("Creating container user: {}", user)
print(f">> Creating container user: {user}")
docker_create_user(container_name, user, uid) docker_create_user(container_name, user, uid)
if not quiet: if config['root_init_command']:
print(f">> Welcome to {container_name} (IP {container['NetworkSettings']['IPAddress']})") config['root_init_commands'].append(config['root_init_command'])
docker_exec(container_name, command, command_user, command_env, command_workdir)
for command in config['root_init_commands']:
docker_exec(container_name, init_command_as_list(command), "root", command_env, tty, "/home/{}".format(command_user))
if config['user_init_command']:
config['user_init_commands'].append(config['user_init_command'])
for command in config['user_init_commands']:
docker_exec(container_name, init_command_as_list(command), command_user, command_env, tty, "/home/{}".format(command_user))
log("Welcome to {} (IP {})", container_name, container['NetworkSettings']['IPAddress'])
docker_exec(container_name, command, command_user, command_env, tty, command_workdir)