Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2aa5672
reverting string to parse config params
marcodelapierre Jan 18, 2022
3b40586
config get: changed behaviour to work with dicts
marcodelapierre Jan 18, 2022
e2b2bf6
reverting config get impl for non-dict, to try and fix tests
marcodelapierre Jan 18, 2022
e809003
adding wrapper script variables
marcodelapierre Jan 18, 2022
7ea838d
fix to updated settings.yml
marcodelapierre Jan 19, 2022
a741017
new default: containers_base distinct from modules_base
marcodelapierre Jan 19, 2022
c59bfa7
1st pass at wrapper_scripts for singularity
marcodelapierre Jan 19, 2022
adc9b37
merged main edits to shpc/client/config.py
marcodelapierre Jan 20, 2022
8fa3ba1
wrapper_script: added tcl for singularity
marcodelapierre Jan 20, 2022
bc6b0e8
wrapper script: added script template for real (was in gitignore)
marcodelapierre Jan 20, 2022
599c442
wrapper script: fix for add case
marcodelapierre Jan 20, 2022
693bdaf
wrapper scripts now created as executable
marcodelapierre Jan 20, 2022
2c35b3e
wrapper scripts: templates for docker/podman
marcodelapierre Jan 20, 2022
9819673
wrapper scripts: bin directory under modules
marcodelapierre Jan 20, 2022
3309d4b
wrapper scripts: docker/podman support
marcodelapierre Jan 20, 2022
9d28e75
Merge branch 'main' into feature/wrapper_scripts
marcodelapierre Jan 28, 2022
6466461
reduced newlines in module templates
marcodelapierre Jan 28, 2022
028a1d6
all shell settings nearby in settings.yml
marcodelapierre Jan 28, 2022
0d43326
utils/fileio: make executable using stat rather than subprocess methods
marcodelapierre Jan 28, 2022
8ece8d1
Merge branch 'main' into feature/wrapper_scripts
marcodelapierre Feb 4, 2022
8646f31
wrapper script templates: protecting argument passing
marcodelapierre Feb 4, 2022
4587b2b
wrapper script templates: protecting argument passing for docker, too
marcodelapierre Feb 14, 2022
26b2b8b
merge main: fix confict in settings.yml
marcodelapierre Feb 14, 2022
7c7179b
merged main, solved conflicts, 15 feb
marcodelapierre Feb 15, 2022
1f03dbd
container: defined wrapper_subdir attribute
marcodelapierre Feb 15, 2022
4cf7aab
modules: defined wrappertemplatefile property
marcodelapierre Feb 15, 2022
be75776
wrapper scripts: relative PATH in modulefile templates
marcodelapierre Feb 15, 2022
cf5b0c3
wrapper_scripts: new function _generate_wrapper_scripts in container/…
marcodelapierre Feb 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion shpc/main/container/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class ContainerTechnology:
# The module technology adds extensions here
modulefile = "module"

# Wrapper scripts are stored in this subdirectory of the module directory
wrapper_subdir = "bin"

# By default, no extra features
features = {}

Expand All @@ -57,7 +60,7 @@ def __init__(self):

self.settings = SettingsBase()

def add(self, sif, module_name, modulefile, template, **kwargs):
def add(self, sif, module_name, modulefile, template, wrapper_template, **kwargs):
"""
Manually add a registry container.
"""
Expand Down Expand Up @@ -175,5 +178,28 @@ def get_features(self, config_features, settings_features, extra=None):

return features

def _generate_wrapper_scripts(self, wrapper_template, aliases, module_dir, features, command=None, container_sif=None, image=None, tty=None):
"""
Generate wrapper scripts for commands (when wrapper_scripts setting enabled)
"""
wrapper_dir = os.path.join(module_dir, self.wrapper_subdir)
shpc.utils.mkdirp([wrapper_dir])
for alias in aliases:
wrapper_path = os.path.join(wrapper_dir, alias['name'])
out = wrapper_template.render(
alias=alias,
bindpaths=self.settings.bindpaths,
command=command,
container_sif=container_sif,
envfile=self.settings.environment_file,
features=features,
image=image,
module_dir=module_dir,
tty=tty,
wrapper_shell=self.settings.wrapper_shell,
)
shpc.utils.write_file(wrapper_path, out, exec=True)
return

def __str__(self):
return str(self.__class__.__name__)
18 changes: 17 additions & 1 deletion shpc/main/container/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def install(
version=None,
config_features=None,
features=None,
wrapper_template=None,
):
"""Install a general container path to a module

Expand All @@ -214,6 +215,19 @@ def install(
# If there's a tag in the name, don't use it
name = name.split(":", 1)[0]

# Option to create wrapper scripts for commands
module_dir = os.path.dirname(module_path)
if self.settings.wrapper_scripts and aliases:
self._generate_wrapper_scripts(
wrapper_template,
aliases,
module_dir,
features,
command=self.command,
image=container_path,
tty=self.settings.enable_tty,
)

# Make sure to render all values!
out = template.render(
podman_module=self.settings.podman_module,
Expand All @@ -223,7 +237,7 @@ def install(
else self.settings.docker_shell,
image=container_path,
description=description,
module_dir=os.path.dirname(module_path),
module_dir=module_dir,
aliases=aliases,
url=url,
features=features,
Expand All @@ -238,5 +252,7 @@ def install(
envfile=self.settings.environment_file,
command=self.command,
tty=self.settings.enable_tty,
wrapper_scripts=self.settings.wrapper_scripts,
wrapper_subdir=self.wrapper_subdir,
)
shpc.utils.write_file(module_path, out)
19 changes: 17 additions & 2 deletions shpc/main/container/singularity.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def get(self, module_name, env_file=False):
logger.exit("Found more than one sif in module folder.")
return sif[0]

def add(self, sif, module_name, modulefile, template, **kwargs):
def add(self, sif, module_name, modulefile, template, wrapper_template, **kwargs):
"""
Manually add a registry container.
"""
Expand Down Expand Up @@ -112,6 +112,7 @@ def add(self, sif, module_name, modulefile, template, **kwargs):
template,
parsed_name=parsed_name,
features=kwargs.get("features"),
wrapper_template=wrapper_template,
)
self.add_environment(module_dir, {}, self.settings.environment_file)
logger.info("Module %s was created." % (module_name))
Expand All @@ -130,6 +131,7 @@ def install(
config_features=None,
features=None,
version=None,
wrapper_template=None,
):
"""Install a general container path to a module

Expand Down Expand Up @@ -166,6 +168,17 @@ def install(
labels = {}
logger.warning("Singularity is not installed, skipping metadata.")

# Option to create wrapper scripts for commands
module_dir = os.path.dirname(module_path)
if self.settings.wrapper_scripts and aliases:
self._generate_wrapper_scripts(
wrapper_template,
aliases,
module_dir,
features,
container_sif=container_path,
)

# Make sure to render all values!
out = template.render(
singularity_module=self.settings.singularity_module,
Expand All @@ -177,7 +190,7 @@ def install(
url=url,
features=features,
version=version,
module_dir=os.path.dirname(module_path),
module_dir=module_dir,
labels=labels,
deffile=deffile,
creation_date=datetime.now(),
Expand All @@ -186,6 +199,8 @@ def install(
registry=parsed_name.registry,
repository=parsed_name.repository,
envfile=self.settings.environment_file,
wrapper_scripts=self.settings.wrapper_scripts,
wrapper_subdir=self.wrapper_subdir,
)
shpc.utils.write_file(module_path, out)

Expand Down
9 changes: 8 additions & 1 deletion shpc/main/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ def modulefile(self):
def templatefile(self):
return "%s.%s" % (self.container.templatefile, self.module_extension)

@property
def wrappertemplatefile(self):
return "%s.%s" % (self.container.templatefile, "sh")

def uninstall(self, name, force=False):
"""
Given a unique resource identifier, uninstall a module
Expand Down Expand Up @@ -147,7 +151,8 @@ def add(self, sif, module_name, **kwargs):
module_name = self.add_namespace(module_name)
template = self._load_template(self.templatefile)
modulefile = os.path.join(self.settings.module_base, module_name.replace(":", os.sep), self.modulefile)
self.container.add(sif, module_name, modulefile, template, **kwargs)
wrapper_template = self._load_template(self.wrappertemplatefile)
self.container.add(sif, module_name, modulefile, template, wrapper_template, **kwargs)

def get(self, module_name, env_file=False):
"""
Expand Down Expand Up @@ -325,6 +330,7 @@ def install(self, name, tag=None, **kwargs):
# Get the template based on the module and container type
template = self._load_template(self.templatefile)
module_path = os.path.join(module_dir, self.modulefile)
wrapper_template = self._load_template(self.wrappertemplatefile)

# If the module has a version, overrides version
version = tag.name
Expand All @@ -344,6 +350,7 @@ def install(self, name, tag=None, **kwargs):
version=version,
config_features=config.features,
features=kwargs.get("features"),
wrapper_template=wrapper_template,
)

# If the container tech does not need storage, clean up
Expand Down
8 changes: 4 additions & 4 deletions shpc/main/modules/templates/docker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ set_shell_function("{|module_name|}-shell", shellCmd, shellCmd)
-- conflict with modules with the same name
conflict("{{ tool }}"{% if name != tool %},"{{ name }}"{% endif %}{% if aliases %}{% for alias in aliases %}{% if alias.name != tool %},"{{ alias.name }}"{% endif %}{% endfor %}{% endif %})

-- exec functions to provide "alias" to module commands
{% if aliases %}{% for alias in aliases %}
set_shell_function("{{ alias.name }}", execCmd .. {% if alias.docker_options %} "{{ alias.docker_options }} " .. {% endif %} " --entrypoint {{ alias.entrypoint }} " .. containerPath .. " {{ alias.args }} \"$@\"", execCmd .. {% if alias.docker_options %} "{{ alias.docker_options }} " .. {% endif %} " --entrypoint {{ alias.entrypoint }} " .. containerPath .. " {{ alias.args }}")
-- "aliases" to module commands
{% if wrapper_scripts %}{% if aliases %}prepend_path("PATH", pathJoin(myFileName():match("(.*[/])") or ".", "{{ wrapper_subdir }}")){% endif %}
{% else %}{% if aliases %}{% for alias in aliases %}set_shell_function("{{ alias.name }}", execCmd .. {% if alias.docker_options %} "{{ alias.docker_options }} " .. {% endif %} " --entrypoint {{ alias.entrypoint }} " .. containerPath .. " {{ alias.args }} \"$@\"", execCmd .. {% if alias.docker_options %} "{{ alias.docker_options }} " .. {% endif %} " --entrypoint {{ alias.entrypoint }} " .. containerPath .. " {{ alias.args }}")
{% endfor %}{% endif %}

{% if aliases %}
if (myShellName() == "bash") then
{% for alias in aliases %}execute{cmd="export -f {{ alias.name }}", modeA={"load"}}
{% endfor %}
end{% endif %}
end{% endif %}{% endif %}

-- A customizable exec function
set_shell_function("{|module_name|}-exec", execCmd .. " --entrypoint \"\" " .. containerPath .. " \"$@\"", execCmd .. " --entrypoint \"\" " .. containerPath)
Expand Down
3 changes: 3 additions & 0 deletions shpc/main/modules/templates/docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!{{ wrapper_shell }}

{{ command }} ${PODMAN_OPTS} run ${PODMAN_COMMAND_OPTS} -i{% if tty %}t{% endif %} -u `id -u`:`id -g` --rm {% if envfile %}--env-file {{ module_dir }}/{{ envfile }}{% endif %} {% if bindpaths %}-v {{ bindpaths }} {% endif %}{% if features.home %}-v {{ features.home }} {% endif %} -v ${PWD} -w ${PWD} {% if alias.docker_options %} {{ alias.docker_options }} {% endif %} --entrypoint {{ alias.entrypoint }} {{ image }} {{ alias.args }} {% if '/sh' in wrapper_shell or '/bash' in wrapper_shell %}"$@"{% elif '/csh' in wrapper_shell %}$argv:q{% endif %}
9 changes: 4 additions & 5 deletions shpc/main/modules/templates/docker.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ set inspectCmd "{{ command }} \${PODMAN_OPTS} inspect ${containerPath}"
# set_shell_function takes bashStr and cshStr
set-alias {|module_name|}-shell "${shellCmd}"

# exec functions to provide "alias" to module commands
{% if aliases %}
if { [ module-info shell bash ] } {
# "aliases" to module commands
{% if wrapper_scripts %}{% if aliases %}prepend-path PATH "[file dirname ${ModulesCurrentModulefile}]/{{ wrapper_subdir }}"{% endif %}
{% else %}{% if aliases %}if { [ module-info shell bash ] } {
if { [ module-info mode load ] } {
{% for alias in aliases %} puts stdout "function {{ alias.name }}() { ${execCmd} {% if alias.docker_options %} {{ alias.docker_options | replace("$", "\$") }} {% endif %} --entrypoint {{ alias.entrypoint | replace("$", "\$") }} ${containerPath} {{ alias.args | replace("$", "\$") }} \"\$@\"; }; export -f {{ alias.name }};"
{% endfor %}
Expand All @@ -87,8 +87,7 @@ if { [ module-info shell bash ] } {
} else {
{% for alias in aliases %} set-alias {{ alias.name }} "${execCmd} {% if alias.docker_options %} {{ alias.docker_options | replace("$", "\$") }} {% endif %} --entrypoint {{ alias.entrypoint | replace("$", "\$") }} ${containerPath} {{ alias.args | replace("$", "\$") }}"
{% endfor %}
}
{% endif %}
}{% endif %}{% endif %}

# A customizable exec function
if { [ module-info shell bash ] } {
Expand Down
8 changes: 4 additions & 4 deletions shpc/main/modules/templates/singularity.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,16 @@ set_shell_function("{|module_name|}-shell", shellCmd, shellCmd)
-- conflict with modules with the same name
conflict("{{ tool }}"{% if name != tool %},"{{ name }}"{% endif %}{% if aliases %}{% for alias in aliases %}{% if alias.name != tool %},"{{ alias.name }}"{% endif %}{% endfor %}{% endif %})

-- exec functions to provide "alias" to module commands
{% if aliases %}{% for alias in aliases %}
set_shell_function("{{ alias.name }}", execCmd .. {% if alias.singularity_options %} "{{ alias.singularity_options }} " .. {% endif %} containerPath .. " {{ alias.command }} \"$@\"", execCmd .. {% if alias.singularity_options %} "{{ alias.singularity_options }} " .. {% endif %} containerPath .. " {{ alias.command }}")
-- "aliases" to module commands
{% if wrapper_scripts %}{% if aliases %}prepend_path("PATH",pathJoin(myFileName():match("(.*[/])") or ".", "{{ wrapper_subdir }}")){% endif %}
{% else %}{% if aliases %}{% for alias in aliases %}set_shell_function("{{ alias.name }}", execCmd .. {% if alias.singularity_options %} "{{ alias.singularity_options }} " .. {% endif %} containerPath .. " {{ alias.command }} \"$@\"", execCmd .. {% if alias.singularity_options %} "{{ alias.singularity_options }} " .. {% endif %} containerPath .. " {{ alias.command }}")
{% endfor %}{% endif %}

{% if aliases %}
if (myShellName() == "bash") then
{% for alias in aliases %}execute{cmd="export -f {{ alias.name }}", modeA={"load"}}
{% endfor %}
end{% endif %}
end{% endif %}{% endif %}

-- A customizable exec function
set_shell_function("{|module_name|}-exec", execCmd .. containerPath .. " \"$@\"", execCmd .. containerPath)
Expand Down
3 changes: 3 additions & 0 deletions shpc/main/modules/templates/singularity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!{{ wrapper_shell }}

singularity ${SINGULARITY_OPTS} exec ${SINGULARITY_COMMAND_OPTS} {% if features.gpu %}{{ features.gpu }} {% endif %}{% if features.home %}-B {{ features.home }} --home {{ features.home }} {% endif %}{% if features.x11 %}-B {{ features.x11 }} {% endif %}{% if envfile %}-B {{ module_dir }}/{{ envfile }}:/.singularity.d/env/{{ envfile }}{% endif %} {% if bindpaths %}-B {{ bindpaths }}{% endif %} {% if alias.singularity_options %} {{ alias.singularity_options }} {% endif %} {{ container_sif }} {{ alias.command }} {% if '/sh' in wrapper_shell or '/bash' in wrapper_shell %}"$@"{% elif '/csh' in wrapper_shell %}$argv:q{% endif %}
9 changes: 4 additions & 5 deletions shpc/main/modules/templates/singularity.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ set inspectCmd "singularity \${SINGULARITY_OPTS} inspect \${SINGULARITY_COMMAND_
# set_shell_function takes bashStr and cshStr
set-alias {|module_name|}-shell "${shellCmd}"

# exec functions to provide "alias" to module commands
{% if aliases %}
if { [ module-info shell bash ] } {
# "aliases" to module commands
{% if wrapper_scripts %}{% if aliases %}prepend-path PATH "[file dirname ${ModulesCurrentModulefile}]/{{ wrapper_subdir }}"{% endif %}
{% else %}{% if aliases %}if { [ module-info shell bash ] } {
if { [ module-info mode load ] } {
{% for alias in aliases %} puts stdout "function {{ alias.name }}() { ${execCmd} {% if alias.singularity_options %} {{ alias.singularity_options | replace("$", "\$") }} {% endif %} ${containerPath} {{ alias.command | replace("$", "\$") }} \"\$@\"; }; export -f {{ alias.name }};"
{% endfor %}
Expand All @@ -92,8 +92,7 @@ if { [ module-info shell bash ] } {
} else {
{% for alias in aliases %} set-alias {{ alias.name }} "${execCmd} {% if alias.singularity_options %} {{ alias.singularity_options | replace("$", "\$") }} {% endif %} ${containerPath} {{ alias.command | replace("$", "\$") }}"
{% endfor %}
}
{% endif %}
}{% endif %}{% endif %}

# A customizable exec function
if { [ module-info shell bash ] } {
Expand Down
2 changes: 2 additions & 0 deletions shpc/main/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,13 @@
"environment_file": {"type": "string"},
"default_version": {"type": "boolean"},
"enable_tty": {"type": "boolean"},
"wrapper_scripts": {"type": "boolean"},
"container_tech": {"type": "string", "enum": ["singularity", "podman", "docker"]},
"singularity_shell": {"type": "string", "enum": shells},
"podman_shell": {"type": "string", "enum": shells},
"docker_shell": {"type": "string", "enum": shells},
"test_shell": {"type": "string", "enum": shells},
"wrapper_shell": {"type": "string", "enum": shells},
"module_sys": {"type": "string", "enum": ["lmod", "tcl", None]},
"container_features": container_features,
}
Expand Down
6 changes: 6 additions & 0 deletions shpc/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ docker_shell: /bin/sh
# shell for test.sh file
test_shell: /bin/bash

# shell for wrapper shellscripts
wrapper_shell: /bin/bash

# for container aliases, use wrapper shellscripts instead of shell aliases, defaults to false
wrapper_scripts: false

# the module namespace you want to install from. E.g., if you have ghcr.io/autamus/clingo
# and you set the namespace to ghcr.io/autamus, you can just do: shpc install clingo.
namespace:
Expand Down
6 changes: 5 additions & 1 deletion shpc/utils/fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import hashlib
import errno
import os
import stat
import re
import shutil
import tempfile
Expand Down Expand Up @@ -114,12 +115,15 @@ def copyfile(source, destination, force=True):
return destination


def write_file(filename, content, mode="w"):
def write_file(filename, content, mode="w", exec=False):
"""
Write content to a filename
"""
with open(filename, mode) as filey:
filey.writelines(content)
if exec:
st = os.stat(filename)
os.chmod(filename, st.st_mode | stat.S_IEXEC)
return filename


Expand Down