Skip to content

Commit

Permalink
Supporting "docker exec" in Genv container toolkit
Browse files Browse the repository at this point in the history
  • Loading branch information
razrotenberg committed May 16, 2024
1 parent fa9a8d8 commit 29628c3
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 21 deletions.
3 changes: 2 additions & 1 deletion genv-docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,5 @@ Use non available devices if needed when set to `1`.
3. https://github.com/opencontainers/runtime-spec/blob/main/glossary.md
4. https://github.com/opencontainers/runc/blob/main/man/runc.8.md
5. https://github.com/opencontainers/runc/blob/main/man/runc-create.8.md
6. https://github.com/NVIDIA/nvidia-container-runtime
6. https://github.com/opencontainers/runc/blob/main/man/runc-exec.8.md
7. https://github.com/NVIDIA/nvidia-container-runtime
68 changes: 48 additions & 20 deletions genv-docker/genv-container-runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,38 +21,59 @@ def find_container_runtime() -> str:
raise RuntimeError("Cannot find any container runtime")


def get_env(config: dict, name: str) -> Optional[str]:
def get_env(process: dict, name: str) -> Optional[str]:
"""
Returns the value of an environment variable if set.
"""

for env in config["process"]["env"]:
for env in process["env"]:
if env.startswith(f"{name}="):
return env.split("=")[-1]


def append_env(config: dict, name: str, value: str) -> None:
def append_env(process: dict, name: str, value: str) -> None:
"""
Appends an environment variable to the configuration.
"""

config["process"]["env"].append(f"{name}={value}")
process["env"].append(f"{name}={value}")


def update_env(config: dict, name: str, mutator: Callable[[str], str]) -> None:
def update_env(process: dict, name: str, mutator: Callable[[str], str]) -> None:
"""
Updates an environment variable.
"""

for index, env in enumerate(config["process"]["env"]):
for index, env in enumerate(process["env"]):
if env.startswith(f"{name}="):
value = env.split("=")[-1]
config["process"]["env"][index] = f"{name}={mutator(value)}"
process["env"][index] = f"{name}={mutator(value)}"
return

raise RuntimeError(f"Cannot update environment variable '{name}'")


def update_process(process: dict, container_id: str) -> None:
"""Updates a process json object.
References:
- https://github.com/opencontainers/runtime-spec/blob/main/config.md#process
"""

append_env(process, "GENV_CONTAINER", "1")

if not get_env(process, "GENV_ACTIVATE") == "0":
if not get_env(process, "GENV_ENVIRONMENT_ID"):
# TODO(raz): consider generating an environment identifier which is different than
# the container identifier to avoid this kind of information leak.
# also note that a short version of the container identifier is set as the container
# hostname then it might be ok.
append_env(process, "GENV_ENVIRONMENT_ID", container_id)

if not get_env(process, "GENV_MOUNT_SHIMS") == "0":
update_env(process, "PATH", lambda value: f"/opt/genv/shims:{value}")


def append_hook(
config: dict,
type: str,
Expand Down Expand Up @@ -92,18 +113,7 @@ def do_create(container_id: str) -> None:
with open("config.json") as f:
config = json.load(f)

append_env(config, "GENV_CONTAINER", "1")

if not get_env(config, "GENV_ACTIVATE") == "0":
if not get_env(config, "GENV_ENVIRONMENT_ID"):
# TODO(raz): consider generating an environment identifier which is different than
# the container identifier to avoid this kind of information leak.
# also note that a short version of the container identifier is set as the container
# hostname then it might be ok.
append_env(config, "GENV_ENVIRONMENT_ID", container_id)

if not get_env(config, "GENV_MOUNT_SHIMS") == "0":
update_env(config, "PATH", lambda value: f"/opt/genv/shims:{value}")
update_process(config["process"], container_id)

# NOTE(raz): even though the hook "prestart" is deprecated, we are using it because the
# nvidia container runtime still uses it itself and we need to make sure we will run before.
Expand All @@ -119,9 +129,27 @@ def do_create(container_id: str) -> None:
json.dump(config, f)


def do_exec(process_json: str, container_id: str) -> None:
"""
Performs the command 'exec'.
"""

with open(process_json) as f:
process = json.load(f)

update_process(process, container_id)

with open(process_json, "w") as f:
json.dump(process, f)


# TODO(raz): prettify error messages
if __name__ == "__main__":
if "create" in sys.argv:
do_create(container_id=sys.argv[-1])

elif "exec" in sys.argv:
do_exec(
process_json=sys.argv[sys.argv.index("--process") + 1],
container_id=sys.argv[-1],
)
subprocess.check_call([find_container_runtime()] + sys.argv[1:])

0 comments on commit 29628c3

Please sign in to comment.