You are on page 1of 8

#!

/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 | www.openfoam.com
# \\/ M anipulation |
#------------------------------------------------------------------------------
# Copyright (C) 2016-2022 OpenCFD Ltd.
#------------------------------------------------------------------------------
# SPDX-License-Identifier: (GPL-3.0+)
#
# Script
# openfoam-docker
#
# Description
# Run script for openfoam container images
# (https://hub.docker.com/u/opencfd)
#
# 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: www.openfoam.com

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/.home.openfoam' # 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
# https://stackoverflow.com/questions/48235040/run-x-application-in-a-docker-
container-reliably-on-a-server-connected-via-ssh-w
# https://blog.yadutaf.fr/2017/09/10/running-a-graphical-app-in-a-docker-container-
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" .Xauthority.container.XXXX)"


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 "$@"

# ---------------------------------------------------------------------------

You might also like