#!
/bin/sh
openfoamVersion="latest"
imageBasename="opencfd/openfoam"
imageFlavour="-run"
scriptVersion="2022-01-10"
#------------------------------------------------------------------------------
# ========= |
# \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
# \\ / O peration |
# \\ / A nd | [Link]
# \\/ M anipulation |
#------------------------------------------------------------------------------
# Copyright (C) 2016-2022 OpenCFD Ltd.
#------------------------------------------------------------------------------
# SPDX-License-Identifier: (GPL-3.0+)
#
# Script
# openfoam-docker
#
# Description
# Run script for openfoam container images
# ([Link]
#
# Copy/link to automatically specify OpenFOAM version and image 'flavour'
# For example,
#
# ln -s openfoam-docker openfoam2112-run
#
#------------------------------------------------------------------------------
printHelp()
{
defaultImage="${imageBasename}${imageFlavour}:${openfoamVersion}"
cat<<HELP_HEAD
Usage: ${0##*/} [OPTION] [-- application ...]
${0##*/} [OPTION] [command_string]
options:
-c Shell commands read from the first non-option argument
-data=DIR Specify mount dir for container '/data'
-dir=DIR Specify mount dir for container '~/' (default: pwd)
-<digits> Specify OpenFOAM version (eg, -2112)
-base | -dev | -run | -tut | -default
Image flavour (default: -run).
Not all image flavours are necessarily available.
-X | -x X11 forwarding: enable (-X) or disable (-x)
-update Update (pull) the image, do not run
-dry-run Report the start command, without running
-verbose Additional verbosity when starting (FOAM_VERBOSE)
HELP_HEAD
if [ -n "$1" ]
then
cat<<'HELP_FULL'
-entry=PATH Alternative entry point
-image=NAME | -i=NAME
Specify image to run
-docker Use docker (default)
-podman Use podman instead of docker
-sudo Prefix container calls with 'sudo'
-xhost Allow container access to host network (implies -X)
--shm-size=BYTES Size of /dev/shm (eg, --shm-size=4G)
HELP_FULL
fi
cat<<TAIL_OPTIONS
-- The end of option processing.
An argument of - or / is equivalent to --.
-h | -help Display short help and exit
-help-full Display full help and exit
Run OpenFOAM container image (${defaultImage})
- Simulations are stored on the host system (not the container)
- Mounts the current or specified directory (except the HOME directory).
Script Version: ${scriptVersion:-[]}
Note:
The user name within the container is 'openfoam',
but matches the user/group id from the host.
Requires docker or podman.
TAIL_OPTIONS
if [ -n "$1" ]
then
cat<<HELP_FULL
Equivalent options:
| -dir=DIR | -case=DIR | -case DIR | -home=DIR
Example:
${0##*/} -dir=/path/to/simulation
or cd /path/to/simulation && ${0##*/}
Note:
The '-xhost' option can be useful in combination with 'ssh -X',
but should be used sparingly since it permits the container full
access to network communication (potentially insecure).
HELP_FULL
fi
cat<<'FOOTER'
Note:
Different OpenFOAM components and modules may be present (or missing)
on any particular container image.
For example, source code, tutorials, in-situ visualization,
paraview plugins, external linear-solver interfaces etc.
For more information: [Link]
FOOTER
exit 0 # A clean exit
}
# Report error and exit
die()
{
exec 1>&2
echo
echo "Error encountered:"
while [ "$#" -ge 1 ]; do echo " $1"; shift; done
echo
echo "See '${0##*/} -help' for usage"
echo
exit 1
}
#------------------------------------------------------------------------------
# Constants - user name/locations MUST correspond to the image assets
toolChain=docker
container_home='/home/openfoam' # Home for container user
container_tmphome='/tmp/.[Link]' # Fake home for container user
#------------------------------------------------------------------------------
# Select 'podman' toolchain if mentioned in script name:
case "${0##*/}" in (*-podman*) toolChain=podman;; esac
# Get openfoam version and/or image flavour from script name
# "/path/openfoam{VERSION}"
# "/path/openfoam{VERSION}-{FLAVOUR}"
imageTag="$(echo "${0##*/}" | sed -ne 's/openfoam\([1-9][0-9]*\).*/\1/ip')"
# Version:
if [ -n "$imageTag" ]
then
openfoamVersion="$imageTag"
fi
# Flavour:
imageTag="-${0##*-}"
case "$imageTag" in
(-base)
unset imageFlavour
;;
(-run | -default)
imageFlavour="$imageTag"
;;
(-dev*)
imageFlavour="-dev"
;;
(-tut*)
imageFlavour="-tutorials"
;;
esac
#------------------------------------------------------------------------------
# Parse options
unset image sudo
unset mount1Dir mount2Dir
unset optDryrun optEntrypoint optShellCommand optUpdate optVerbose optShmSize
unset optX11Forwarding
while [ "$#" -gt 0 ]
do
case "$1" in
('') ;;
(- | -- | /)
shift
break # Stop option parsing
;;
# OpenFOAM versions (eg, -2112, -2106, -2012, etc)
(-[1-9]*) openfoamVersion="${1#*-}" ;;
(-v[1-9]*) openfoamVersion="${1#*-v}" ;;
# Image flavours
(-base) unset imageFlavour ;;
(-run) imageFlavour="-run" ;;
(-def*) imageFlavour="-default" ;;
(-dev*) imageFlavour="-dev" ;;
(-tut*) imageFlavour="-tutorials" ;;
(-c) # Shell command
optShellCommand="-c"
;;
(-help-f*) # Full help
printHelp -full
;;
(-h | -help* | --help*) # Short help
printHelp
;;
(-docker | -podman)
toolChain="${1#*-}"
;;
(-sudo) # Use sudo
sudo="sudo"
;;
(--shm-size=*)
optShmSize="${1#*=}"
;;
(-case)
# OpenFOAM-style '-case' for specfying the HOME mount
[ "$#" -ge 2 ] || die "'$1' option requires an argument"
shift
mount1Dir="$1"
;;
# Various ways to specify the HOME mount
(-case=* | -dir=* | -home=*)
mount1Dir="${1#*=}"
;;
# Additional DATA mount
(-data=*)
mount2Dir="${1#*=}"
;;
(-entry=*) # Alternative entrypoint
optEntrypoint="${1#*=}"
;;
(-i=* | -image=*) # Alternative image name
image="${1#*=}"
;;
(-update | -upgrade) # Also allow 'upgrade' (for Ubunutu people)
optUpdate=true
;;
(-dry-run | -dryrun)
optDryrun=true
;;
(-verbose)
optVerbose=true
;;
(-X) # Enable X11 forwarding
: "${optX11Forwarding:=X}"
;;
(-x) # Disable X11 forwarding
unset optX11Forwarding
;;
(-xhost) # Any host X11 forwarding
optX11Forwarding="host"
;;
(-*)
die "Invalid option '$1'"
;;
(*)
if [ -n "$optShellCommand" ]
then
break
else
die "Unexpected argument '$1'"
fi
;;
esac
shift
done
if [ -z "$image" ]
then
image="${imageBasename}${imageFlavour}:${openfoamVersion}"
fi
if [ -n "$optUpdate" ]
then
if [ -n "$optDryrun" ]
then
runPrefix="echo"
echo "(dry-run)" 1>&2
echo 1>&2
else
runPrefix="$sudo"
echo "Update image: $image" 1>&2
fi
$runPrefix ${toolChain:?} pull "$image"
exitCode="$?"
echo 1>&2
echo "Done" 1>&2
exit "$exitCode"
fi
if [ -n "$optShellCommand" ] && [ "$#" -eq 0 ]
then
die "-c: option requires an argument"
fi
#------------------------------------------------------------------------------
# Sanity and setup
guest_uid="$(id -u 2>/dev/null)"
guest_gid="$(id -g 2>/dev/null)"
[ -n "$guest_uid" ] || die "Cannot determine current user id"
[ -n "$guest_gid" ] || die "Cannot determine current group id"
# Mount directory (mandatory)
if [ -z "$mount1Dir" ]
then
mount1Dir="$(pwd -P)" # Default is current directory
elif [ -d "$mount1Dir" ]
then
mount1Dir="$(cd "$mount1Dir" && pwd -P)"
else
die "No such mount directory: $mount1Dir"
fi
if [ "$mount1Dir" = "$(cd "$HOME" && pwd -P)" ]
then
die "Cannot use home directory as the mount-point" \
"Run from a subdirectory instead"
fi
# Data directory (optional)
if [ -n "$mount2Dir" ]
then
if [ -d "$mount2Dir" ]
then
mount2Dir="$(cd "$mount2Dir" && pwd -P)"
else
echo "${0##*/}: ignore invalid -data directory: $mount2Dir" 1>&2
unset mount2Dir
fi
fi
# Older (non-nss) user/group handling
# userMapping()
# {
# echo '--volume=/etc/group:/etc/group:ro'
# echo '--volume=/etc/passwd:/etc/passwd:ro'
# echo '--volume=/etc/shadow:/etc/shadow:ro'
# echo '--volume=/etc/sudoers.d:/etc/sudoers.d:ro'
# }
# Environment and settings for X11 forwarding
#
# See Also
# [Link]
container-reliably-on-a-server-connected-via-ssh-w
# [Link]
on-a-remote-server/
# Prepare X11 mapping
# - use tmp Xauthority file in local (mounted) directory
unset display_host tmpXauth_host tmpXauth_guest
if [ -n "$DISPLAY" ] \
&& [ -n "$optX11Forwarding" ] \
&& [ -d "$mount1Dir" ] \
&& command -v xauth >/dev/null
then
# DISPLAY="host:0" vs DISPLAY=":0"
display_host="${DISPLAY%%:*}"
tmpXauth_host="$(mktemp --tmpdir="$mount1Dir" .[Link])"
trap "rm -f \"$tmpXauth_host\"; exit 0" EXIT TERM INT # Remove on exit
# X-authority file to allow any hostname. Generally reasonably safe
xauth nlist "$DISPLAY" | sed -e 's/^..../ffff/' | \
xauth -f "$tmpXauth_host" nmerge -
# The mounted name
tmpXauth_guest="${container_home}/${tmpXauth_host##*/}"
fi
# Define container run options for X11 mapping
x11Mapping()
{
if [ -n "$tmpXauth_guest" ]
then
echo "--env=DISPLAY=$DISPLAY"
echo "--env=XAUTHORITY=$tmpXauth_guest"
# Accessing via ssh -X ('localhost:0' etc) or forced with -xhost
if [ -n "$display_host" ] || [ "$optX11Forwarding" = host ]
then
echo "--net=host"
elif [ -d /tmp/.X11-unix ] # No display host, bind sockets
then
echo "--volume=/tmp/.X11-unix:/tmp/.X11-unix"
fi
fi
}
if [ "$optVerbose" = true ]
then
set -x
fi
if [ -n "$optDryrun" ]
then
runPrefix="echo"
echo "(dry-run)" 1>&2
echo 1>&2
else
runPrefix="$sudo"
fi
if [ "$optVerbose" = true ]
then
set -x
fi
exec $runPrefix ${toolChain:?} run \
--rm -t -i \
--user="$guest_uid:$guest_gid" \
${mount1Dir:+--volume="$mount1Dir:$container_home"} \
${mount2Dir:+--volume="$mount2Dir:/data"} \
$(x11Mapping) \
${optShmSize:+--shm-size="$optShmSize"} \
${optEntrypoint:+--entrypoint="$optEntrypoint"} \
"$image" $optShellCommand "$@"
# ---------------------------------------------------------------------------