Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

piggy-back on apptainer SIF image cache #143

Merged
merged 5 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 15 additions & 6 deletions ci/test
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,19 @@ if [ -z "${runners}" ]; then
fi

for runner in ${runners}; do
_denv_info "Testing denv with '${runner}'"
if [ "${runner}" = "norunner" ]; then
DENV_RUNNER="${runner}" ./test/bats/bin/bats --filter-tags "norunner" test/
else
DENV_RUNNER="${runner}" ./test/bats/bin/bats test/
fi
filter_tags=""
case "${runner}" in
norunner)
filter_tags="--filter-tags norunner"
;;
docker|podman)
filter_tags="--filter-tags !apptainer"
;;
*)
filter_tags=""
;;
esac
_denv_info "Testing denv with '${runner}' ${filter_tags}"
# shellcheck disable=SC2068
DENV_RUNNER="${runner}" ./test/bats/bin/bats ${filter_tags} test/
done
19 changes: 13 additions & 6 deletions denv
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,27 @@ _denv_pull() {
if [ -e "${denv_image}" ]; then
_denv_info "This path exists on the filesystem, assuming unpacked image."
# if the passed image is a file system path,
# we assume it is an already unpacked image
# we assume it is an image (unpacked or SIF)
# and so we just symlink to it in our cache
ln -s "${denv_image}" "${sif_file}"
ln -sf "${denv_image}" "${sif_file}"
else
_denv_info "Image does not exist on filesystem, assuming registry tag."
# passed image is not a file so we assume it
# is the name of an image in some remote registry
# using the docker:// registry as default
image_full="${denv_image}"
echo "${denv_image}" | grep -v '://' && image_full="docker://${denv_image}"
${denv_runner} build \
--force \
"${sif_file}" \
"${image_full}"
if [ "${denv_runner}" = "apptainer" ]; then
container_env_var="APPTAINER_CONTAINER"
else
container_env_var="SINGULARITY_CONTAINER"
fi
# we pass the full image name including registry to be run by apptainer/singularity
# this then allows us to retrieve the full path to the cached SIF image file
# that is constructed from the OCI image
# then we link this cached image file to our local image cache
cached_image=$(${denv_runner} run "${image_full}" printenv "${container_env_var}")
ln -sf "${cached_image}" "${sif_file}"
fi
;;
norunner)
Expand Down
43 changes: 41 additions & 2 deletions docs/src/tune.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Updating this file with changes to `*PATH` variables (like `PATH`, `LD_LIBRARY_P
and `PYTHONPATH`) can be helpful so that executables can be run interactively
(i.e. `denv` then `my-executable`) and non-interactively (i.e. `denv my-executable`).

## extra mounts
## Extra Mounts
By default, `denv` only allows the container to view the files within
the workspace[^1]. This works for many projects; however,
sometimes software being developed requires input data from outside
Expand All @@ -82,7 +82,7 @@ when it is being run.
denv config mounts /path/to/extra/data/outside/workspace
```
`denv` requires any additional mounts to be specified by their full path
and to already exist. This prevents user typos as well as insures the user
and to already exist. This prevents user typos as well as ensures the user
knows what path will be available within the denv (i.e. symlinks _outside_
the denv may not map properly _inside_ the denv).

Expand All @@ -97,6 +97,45 @@ stay that value regardless on what the value of that variable is on the host.
Some examples of using this behavior are provided
in the EXAMPLES section of the [denv config manual](manual/denv-config.md).

## Cluster Computing

_Note:_ In this section, to avoid typing, I'm going to refer to the
Apptainer/SyLabs SingularityCE/Singularity group of runners as `appatainer`
and the Docker/Podman group of runners as `docker`.

On many computing clusters, `apptainer` is the default container runner
installed and used by `denv` and, in order to make the usage of `apptainer`
(via `denv`) the same as `docker`, we reference images using the OCI
image name (`[registry/]owner/repo:tag`).
For example
```
denv init python:3.12
```
is the same on personal computers with `docker` and remote clusters
with `apptainer`.
We achieve this mimicry by piggy-backing on the `apptainer` cache directory
where `apptainer` stores an intermediate SIF image it builds from the OCI image
we provide it.
This has two large effects for users of `denv` on computing clusters.
1. If the default cache location within users' home directories is too small to
hold the images you want them to be using, they should update their shell configuration
to define `APPTAINER_CACHEDIR` (or `SINGULARITY_CACHEDIR` for the `singularity` runners)
to a different location with more space, preferably a place that supports atomic rename.
2. If you plan to run many parallel jobs using the same image, you should pre-build
a SIF image using `apptainer pull` and provide this image to `denv` (in `denv init`
or `denv config image`) so that `denv` uses a frozen image during parallel processing
rather than referencing the cache which the user could change while the parallel
jobs are running leading to potential differences.

These comments may apply to the `docker` family of runners especially if more
clusters adopt a configuration where both Podman and Apptainer are installed.
(For example, [Containers on HPC](https://github.com/dirkpetersen/hpc-containers)
proposes a configuration.)
Personally, I have yet to gain access to a cluster where Podman is installed and
configured in a way usable by `denv`. I have accessed a cluster where both Podman
and Apptainer are installed but Podman is not given access to user namespaces and
thus we are unable to pretend to be the correct user in a Podman-launched container.

[^1]: This isn't exactly true. denv also mounts a few helper files as well
(e.g. the entrypoint program `_denv_entrypoint`); however, those are single-file
mounts that can be ignored by normal users.
37 changes: 37 additions & 0 deletions test/apptainer.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bats

setup() {
load "test_helper/denv"
common_setup

go_to_tmp_work
denv init alpine:latest
}

teardown() {
clean_tmp_work
}

# bats file_tags=apptainer

@test "normal running works" {
run -0 denv true
}

@test "can invalidate cache and then run" {
case "${DENV_RUNNER}" in
apptainer)
cache=${APPTAINER_CACHEDIR:-${HOME}/.apptainer/cache}
;;
singularity)
cache=${SINGULARITY_CACHEDIR:-${HOME}/.singularity/cache}
;;
*)
echo "This test is only designed for apptainer/singularity, not ${DENV_RUNNER}."
echo "Re-run with '--filter-tags !apptainer' to test ${DENV_RUNNER}"
false
;;
esac
rm -r ${cache}
run -0 denv true
}