From f3da8d330dfc9a8e298bc944a97aec9a4d8e9f5a Mon Sep 17 00:00:00 2001 From: Klaus Reuter <khr@mpcdf.mpg.de> Date: Thu, 25 Jan 2024 11:55:48 +0100 Subject: [PATCH] transfer environment name from user's environment.yml --- condainer/condainer.py | 61 ++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/condainer/condainer.py b/condainer/condainer.py index 70a8112..f65649f 100644 --- a/condainer/condainer.py +++ b/condainer/condainer.py @@ -54,10 +54,10 @@ def write_example_environment_yml(): fp.write(environment_yml) -def write_cfg(cfg): +def write_cfg(cfg, cfg_yml='condainer.yml'): """Write config dictionary to YAML. """ - with open('condainer.yml', 'w') as fp: + with open(cfg_yml, 'w') as fp: fp.write("# Condainer project configuration file\n") fp.write("#\n") fp.write("# - initially created by `condainer init`\n") @@ -67,15 +67,15 @@ def write_cfg(cfg): fp.write(yaml.safe_dump(cfg, sort_keys=False)) -def get_cfg(): - """Read the config dictionary from YAML, and return. +def get_cfg(cfg_yml='condainer.yml'): + """Read a config dictionary from YAML, and return. """ - with open('condainer.yml', 'r') as fp: + with open(cfg_yml, 'r') as fp: cfg = yaml.safe_load(fp) return cfg -def get_env_directory(cfg): +def get_base_env_directory(cfg): """Determine and return the base directory of the environment (which is identical to the squashfuse mount point). """ if cfg.get('multiuser_mountpoint'): @@ -88,6 +88,12 @@ def get_env_directory(cfg): return os.path.join(cfg['mount_base_directory'], "condainer-"+cfg['uuid']+suffix) +def get_user_env_directory(cfg): + """Determine and return the directory of the nested conda environment. + """ + return os.path.join(get_base_env_directory(cfg), "envs", cfg["user_env_name"]) + + def get_installer_path(cfg): """Return the path to the Miniforge installer, either the full path including the filename, or the filename alone, assuming that it has been downloaded to the Condainer project directory already. @@ -101,7 +107,7 @@ def get_installer_path(cfg): def is_mounted(cfg): """Return True if the container is mounted at its respective mountpoint, False otherwise. """ - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) q = False with open('/proc/mounts', 'r') as fp: for raw in fp: @@ -121,9 +127,10 @@ def get_image_filename(cfg): def get_activate_cmd(cfg): """Return the shell command necessary to `activate` the condainer environment. """ - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) activate = os.path.join(os.path.join(env_directory, 'bin'), 'activate') - return f"source {activate} condainer" + user_env_name = cfg["user_env_name"] + return f"source {activate} {user_env_name}" def write_activate_script(cfg): @@ -154,7 +161,7 @@ def write_deactivate_script(cfg): def get_lockfilename(cfg): """Return lock file name unique to the present project and host name. """ - return get_env_directory(cfg)+"-"+socket.gethostname()+".mutex" + return get_base_env_directory(cfg)+"-"+socket.gethostname()+".mutex" def acquire_lock(lock_file): @@ -181,7 +188,7 @@ def create_base_environment(cfg): """Create base environment. """ conda_installer = get_installer_path(cfg) - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) cmd = f"/bin/bash {conda_installer} -b -f -p {env_directory}".split() env = copy.deepcopy(os.environ) if "PYTHONPATH" in env: @@ -202,10 +209,12 @@ def create_base_environment(cfg): def create_condainer_environment(cfg): """Install user-defined software stack (environment.yml) into 'condainer' environment. """ - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) exe = os.path.join(os.path.join(env_directory, 'bin'), cfg['conda_exe']) environment_yml = cfg["environment_yml"] - cmd = f"{exe} env create --file {environment_yml} --name condainer".split() + environment_cfg = get_cfg(environment_yml) + user_env_name = environment_cfg.get("name", "env") + "@condainer" + cmd = f"{exe} env create --file {environment_yml} --name {user_env_name}".split() env = copy.deepcopy(os.environ) if "PYTHONPATH" in env: del env["PYTHONPATH"] @@ -215,13 +224,14 @@ def create_condainer_environment(cfg): proc = subprocess.Popen(cmd, shell=False, env=env) proc.communicate() assert(proc.returncode == 0) + cfg["user_env_name"] = user_env_name + write_cfg(cfg) def pip_condainer_environment(cfg): """Install user-defined software stack (requirements.txt) into 'condainer' environment. """ - env_directory = get_env_directory(cfg) - exe = os.path.join(os.path.join(env_directory, 'bin'), 'pip3') + exe = os.path.join(get_user_env_directory(cfg), 'bin', 'pip3') requirements_txt = cfg["requirements_txt"] if os.path.isfile(requirements_txt): cmd = f"{exe} install --requirement {requirements_txt} --no-cache-dir".split() @@ -242,7 +252,7 @@ def pip_condainer_environment(cfg): def clean_environment(cfg): """Delete pkg files and other unnecessary files from base environment. """ - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) exe = os.path.join(os.path.join(env_directory, 'bin'), cfg['conda_exe']) cmd = f"{exe} clean --all --yes".split() env = copy.deepcopy(os.environ) @@ -271,7 +281,7 @@ def get_squashfs_num_threads(): def compress_environment(cfg, read_only_flags=True): """Create squashfs image from base environment. """ - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) # explicitly set read-only flags before compressing if read_only_flags: cmd = f"chmod -R a-w {env_directory}".split() @@ -306,11 +316,10 @@ def run_cmd(args, cwd): """Run command in a sub-process, where PATH is prepended with the 'bin' directory of the 'condainer' environment in the container. """ cfg = get_cfg() - env_directory = get_env_directory(cfg) if cfg.get('non_conda_application'): - bin_directory = os.path.join(env_directory, 'bin') + bin_directory = os.path.join(get_base_env_directory(cfg), 'bin') else: - bin_directory = os.path.join(env_directory, 'envs', 'condainer', 'bin') + bin_directory = os.path.join(get_user_env_directory(cfg), 'bin') env = copy.deepcopy(os.environ) env['PATH'] = bin_directory + ':' + env['PATH'] if args.dryrun: @@ -379,7 +388,7 @@ def init(args): if not args.dryrun: write_example_environment_yml() else: - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) os.makedirs(env_directory, exist_ok=True, mode=0o700) print(env_directory) @@ -391,7 +400,7 @@ def build(args): cfg["quiet"] = args.quiet cfg["dryrun"] = args.dryrun squashfs_image = get_image_filename(cfg) - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) if os.path.isfile(squashfs_image): print(f"STOP. Found existing image file {squashfs_image}, please remove this first.") sys.exit(1) @@ -457,7 +466,7 @@ def mount(args): if (not args.quiet) and (): print("hint: condainer already mounted") else: - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) os.makedirs(env_directory, exist_ok=True, mode=0o700) squashfs_image = get_image_filename(cfg) cmd = f"squashfuse {squashfs_image} {env_directory}".split() @@ -475,7 +484,7 @@ def mount(args): # print(termcol.BOLD+"OK"+termcol.ENDC) # print feature necessary for the dynamic mount directory feature within the activate script if args.print: - print(get_env_directory(cfg)) + print(get_base_env_directory(cfg)) def umount(args): @@ -483,7 +492,7 @@ def umount(args): """ cfg = get_cfg() if is_mounted(cfg): - env_directory = get_env_directory(cfg) + env_directory = get_base_env_directory(cfg) cmd = f"fusermount -u {env_directory}".split() if args.dryrun: print(f"dryrun: {' '.join(cmd)}") @@ -537,7 +546,7 @@ def status(args): print(termcol.BOLD+"Condainer status"+termcol.ENDC) print(f" - project directory : {os.getcwd()}") print(f" - squashfs image : {get_image_filename(cfg)}") - print(f" - fuse mount point : {get_env_directory(cfg)}") + print(f" - fuse mount point : {get_base_env_directory(cfg)}") print(f" - image mounted : {is_mounted(cfg)}") -- GitLab