Skip to content
Merged
15 changes: 12 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jobs:
printf "\n\nmodule help ============================================\n"
module help python/3.9.5-alpine

set -x
python-exec echo donuts >test_output
cat test_output
grep --quiet donuts test_output
Expand All @@ -109,13 +110,14 @@ jobs:
cat test_output
grep --quiet 'Python 3.9.5' test_output
rm test_output
shpc uninstall --force python:3.9.5-alpine

# Try creating views install
mkdir -p tmp-modules
shpc config set views_base:tmp-modules
shpc view create noodles
shpc install --view noodles python:3.9.5-alpine
shpc view install noodles python:3.9.5-alpine

shpc uninstall --force python:3.9.5-alpine
shpc view --force delete noodles

- name: Run python module tests (tcsh)
shell: tcsh -e {0}
Expand Down Expand Up @@ -164,4 +166,11 @@ jobs:
cat test_output
grep --quiet 'Python 3.9.5' test_output
rm test_output

mkdir -p tmp-modules
shpc config set views_base:tmp-modules
shpc view create noodles
shpc view install noodles python:3.9.5-alpine

shpc uninstall --force python:3.9.5-alpine
shpc view --force delete noodles
6 changes: 0 additions & 6 deletions shpc/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,6 @@ def get_parser():
"install_recipe",
help="recipe to install\nshpc install python\nshpc install python:3.9.5-alpine",
)
install.add_argument(
"--view",
dest="view",
help="install module to a named view (must be installed to shpc first).",
default=None,
)
install.add_argument(
"--no-view",
dest="no_view",
Expand Down
13 changes: 5 additions & 8 deletions shpc/client/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
__license__ = "MPL 2.0"

import shpc.utils
from shpc.logger import logger


def main(args, parser, extra, subparser):
Expand All @@ -25,11 +24,9 @@ def main(args, parser, extra, subparser):
# Update config settings on the fly
cli.settings.update_params(args.config_params)

# It doesn't make sense to define view and no view
if args.view and args.no_view:
logger.exit("Conflicting arguments --view and --no-view, choose one.")

# And do the install
cli.install(
args.install_recipe, view=args.view, disable_view=args.no_view, force=args.force
)
cli.install(args.install_recipe, force=args.force)
if cli.settings.default_view and not args.no_view:
cli.view_install(
cli.settings.default_view, args.install_recipe, force=args.force
)
10 changes: 7 additions & 3 deletions shpc/client/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ def create_from_file(

# Extra modules to install
for install_module in install_modules:
cli.install(install_module, view=view_name, disable_view=False, force=force)

# TODO: can we cut out early if already installed?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll keep that in mind when implementing reinstall and upgrade. I could split force into specific flags (like ignore-missing and uninstall-missing in upgrade)

cli.install(install_module, force=force)
cli.view_install(view_name, install_module, force=force)


def main(args, parser, extra, subparser):
Expand Down Expand Up @@ -168,7 +171,8 @@ def main(args, parser, extra, subparser):
# We don't make it hard to require them to install to the root first
module_name = args.params.pop(0)
if command == "install":
cli.install(module_name, view=view_name, disable_view=False, force=args.force)
cli.install(module_name, force=args.force)
cli.view_install(view_name, module_name, force=args.force)

if command == "uninstall":
cli.uninstall(module_name, view=view_name, force=args.force)
cli.view_uninstall(view_name, module_name, force=args.force)
8 changes: 5 additions & 3 deletions shpc/main/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ def get_client(quiet=False, **kwargs):

# Add the container operator
if container == "singularity":
from .container import SingularityContainer
from shpc.main.container import SingularityContainer

Client.container = SingularityContainer()

elif container == "podman":
from .container import PodmanContainer
from shpc.main.container import PodmanContainer

Client.container = PodmanContainer()

elif container == "docker":
from .container import DockerContainer
from shpc.main.container import DockerContainer

Client.container = DockerContainer()

Expand All @@ -64,6 +64,8 @@ def get_client(quiet=False, **kwargs):
logger.warning(
"%s is not installed, functionality might be limited." % container.upper()
)

# Pass on settings and container to module too
Client.quiet = quiet
Client.settings = settings
return Client()
43 changes: 12 additions & 31 deletions shpc/main/container/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,7 @@ def test_script(self, image, test_script):
# Return code
return result["return_code"]

def install(
self,
module_path,
container_path,
name,
template,
parsed_name,
aliases=None,
url=None,
description=None,
version=None,
config_features=None,
features=None,
config=None,
):
def install(self, module_path, template, module, features=None):
"""Install a general container path to a module

The module_dir should be created by the calling function, and
Expand All @@ -210,50 +196,45 @@ def install(
# Container features are defined in container.yaml and the settings
# and specific values are determined by the container technology
features = self.get_features(
config_features, self.settings.container_features, features
module.config.features, self.settings.container_features, features
)

# Ensure that the container exists
# Do we want to clean up other versions here too?
manifest = self.inspect(container_path)
manifest = self.inspect(module.container_path)
if not manifest:
sys.exit("Container %s was not found. Was it pulled?" % container_path)
sys.exit(
"Container %s was not found. Was it pulled?" % module.container_path
)

labels = manifest[0].get("Labels", {})

# 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)
aliases = module.config.get_aliases()
wrapper_scripts = []

# Wrapper scripts can be global (for aliases) or container specific
if self.settings.wrapper_scripts["enabled"] is True:
wrapper_scripts = shpc.main.wrappers.generate(
aliases=aliases,
module_dir=module_dir,
module_dir=module.module_dir,
features=features,
container=self,
image=container_path,
config=config,
image=module.container_path,
config=module.config,
)

# Make sure to render all values!
out = template.render(
settings=self.settings,
shell=self.shell_path,
image=container_path,
description=description,
aliases=aliases,
url=url,
features=features,
version=version,
labels=labels,
creation_date=datetime.now(),
name=name,
parsed_name=parsed_name,
command=self.command,
module=module,
parsed_name=module.config.name,
wrapper_scripts=wrapper_scripts,
)
shpc.utils.write_file(module_path, out)
41 changes: 11 additions & 30 deletions shpc/main/container/singularity.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,7 @@ def _add_docker_image(self, name, tag, image, config, container_yaml, **kwargs):
config.add_tag(tag, tags[tag])
return config

def install(
self,
module_path,
container_path,
name,
template,
parsed_name,
aliases=None,
template_name=None,
url=None,
description=None,
config_features=None,
features=None,
version=None,
config=None,
):
def install(self, module_path, template, module, features=None):
"""Install a general container path to a module

The module_dir should be created by the calling function, and
Expand All @@ -191,19 +176,19 @@ def install(
# Container features are defined in container.yaml and the settings
# and specific values are determined by the container technology
features = self.get_features(
config_features, self.settings.container_features, features
module.config.features, self.settings.container_features, features
)

# Remove any previous containers
container_dir = os.path.dirname(container_path)
container_dir = os.path.dirname(module.container_path)
for older in glob("%s%s*.sif" % (container_dir, os.sep)):
if older == container_path:
if older == module.container_path:
continue
os.remove(older)

# Get inspect metadata from the container (only if singularity installed
try:
metadata = self.inspect(container_path)
metadata = self.inspect(module.container_path)

# Add labels, and deffile
labels = metadata.get("attributes", {}).get("labels")
Expand All @@ -216,34 +201,30 @@ def install(
labels = {}

# Option to create wrapper scripts for commands
module_dir = os.path.dirname(module_path)
aliases = module.config.get_aliases()

# Wrapper scripts can be global (for aliases) or container specific
wrapper_scripts = []
if self.settings.wrapper_scripts["enabled"] is True:
wrapper_scripts = shpc.main.wrappers.generate(
aliases=aliases,
module_dir=module_dir,
module_dir=module.module_dir,
features=features,
container=self,
image=container_path,
config=config,
image=module.container_path,
config=module.config,
)

# Make sure to render all values!
out = template.render(
settings=self.settings,
container_sif=container_path,
description=description,
aliases=aliases,
url=url,
features=features,
version=version,
labels=labels,
deffile=deffile,
creation_date=datetime.now(),
name=name,
parsed_name=parsed_name,
module=module,
parsed_name=module.config.name,
wrapper_scripts=wrapper_scripts,
)
utils.write_file(module_path, out)
Expand Down
Loading