Skip to content
Snippets Groups Projects
openfoam-docker 11 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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-or-later)
    
    #     Run script for openfoam container images
    
    #     (https://hub.docker.com/u/opencfd)
    #
    #     Copy/link to automatically specify OpenFOAM version and image 'flavour'
    #     For example,
    #
    
    Mark OLESEN's avatar
    Mark OLESEN committed
    #        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)
    
    Mark OLESEN's avatar
    Mark OLESEN committed
      -<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
            ;;
    
    
    Mark OLESEN's avatar
    Mark OLESEN committed
        # 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
    
        (-h | -help* | --help*)  # Short help
    
        (-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=*)
    
        (-entry=*)            # Alternative entrypoint
    
        (-i=* | -image=*)     # Alternative image name
    
        (-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"
            ;;
    
    
            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 "$@"
    
    # ---------------------------------------------------------------------------